<!ENTITY pathconfig.SAMBA_DATADIR '\${prefix}/var/samba'>
<!ENTITY pathconfig.CTDB_DATADIR '\${prefix}/share/ctdb'>
<!ENTITY pathconfig.CONFIGFILE '\${prefix}/etc/smb.conf'>
-<!ENTITY pathconfig.HIMMELBLAUD_HSM_PIN_PATH '\${prefix}/var/lib/himmelblaud/hsm-pin'>
"
+++ /dev/null
-<samba:parameter name="himmelblaud hello enabled"
- context="G"
- type="boolean"
- xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
-<description>
-
- <para>This parameter controls Hello enrollment and authentication to
- Azure Entra ID. By default, it is disabled to prevent security risks,
- such as on hosts exposing the SSH port. Administrators should enable
- this setting only when Hello enrollment is appropriate for their
- environment.
-
-</para>
-</description>
-
-<value type="default">no</value>
-<value type="example">yes</value>
-</samba:parameter>
+++ /dev/null
-<samba:parameter name="himmelblaud hsm pin path"
- context="G"
- type="string"
- xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
-<description>
- <para>Specifies the file path where the HSM PIN is stored. This PIN is used
- for unlocking TPM objects required for Azure Entra ID authentication. The HSM
- PIN is critical for ensuring secure communication and authentication within
- the Himmelblaud daemon.</para>
-</description>
-
-<value type="default">&pathconfig.HIMMELBLAUD_HSM_PIN_PATH;</value>
-</samba:parameter>
+++ /dev/null
-<samba:parameter name="himmelblaud sfa fallback"
- context="G"
- type="boolean"
- xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
-<description>
-
- <para>This parameter is designed to control whether Himmelblaud should fallback to
- Single Factor Authentication (SFA) if Multi-Factor Authentication (MFA) isn't
- available. This normally is possible during a short window during which MFA
- enrollment is available to a new user.
-
-</para>
-</description>
-
-<value type="default">no</value>
-<value type="example">yes</value>
-</samba:parameter>
DEFINE_DYN_CONFIG_PARAM(PYTHONDIR)
DEFINE_DYN_CONFIG_PARAM(PYTHONARCHDIR)
DEFINE_DYN_CONFIG_PARAM(SCRIPTSBINDIR)
-DEFINE_DYN_CONFIG_PARAM(HIMMELBLAUD_HSM_PIN_PATH)
DEFINE_DYN_CONFIG_PROTO(PYTHONDIR)
DEFINE_DYN_CONFIG_PROTO(PYTHONARCHDIR)
DEFINE_DYN_CONFIG_PROTO(SCRIPTSBINDIR)
-DEFINE_DYN_CONFIG_PROTO(HIMMELBLAUD_HSM_PIN_PATH)
'HELPTEXT': 'Where to put the smbpasswd file',
'DELAY': True,
},
- 'HIMMELBLAUD_HSM_PIN_PATH': {
- 'STD-PATH': '${LOCALSTATEDIR}/lib/himmelblaud/hsm-pin',
- 'FHS-PATH': '${LOCALSTATEDIR}/lib/himmelblaud/hsm-pin',
- 'OPTION': '--with-himmelblaud-hsm-pin-path',
- 'HELPTEXT': 'Where to store the hsm pin',
- 'DELAY': True,
- },
}
def options(opt):
"acl claims evaluation",
"AD DC only");
- /* Set the default Himmelblaud globals */
- lpcfg_do_global_parameter(lp_ctx,
- "himmelblaud hsm pin path",
- get_dyn_HIMMELBLAUD_HSM_PIN_PATH());
- lpcfg_do_global_parameter(lp_ctx,
- "himmelblaud hello enabled",
- "false");
- lpcfg_do_global_parameter(lp_ctx,
- "himmelblaud sfa fallback",
- "false");
-
lpcfg_do_global_parameter(lp_ctx,
"server smb encryption over quic",
"yes");
+++ /dev/null
-[source.crates-io]
-replace-with = "vendored-sources"
-
-[source.vendored-sources]
-directory = "../bin/default/rust/vendor"
+++ /dev/null
-# ---> Rust
-# Generated by Cargo
-# will have compiled files and executables
-debug/
-target/
-
-# These are backup files generated by rustfmt
-**/*.rs.bk
-
-# MSVC Windows builds of rustc generate these, which store debugging information
-*.pdb
-
-vendor/
-tags
+++ /dev/null
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "addr2line"
-version = "0.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-
-[[package]]
-name = "ahash"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
-dependencies = [
- "cfg-if",
- "getrandom",
- "once_cell",
- "version_check",
- "zerocopy",
-]
-
-[[package]]
-name = "aho-corasick"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "anstream"
-version = "0.6.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
-dependencies = [
- "anstyle",
- "anstyle-parse",
- "anstyle-query",
- "anstyle-wincon",
- "colorchoice",
- "is_terminal_polyfill",
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle"
-version = "1.0.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
-
-[[package]]
-name = "anstyle-parse"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
-dependencies = [
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle-query"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
-dependencies = [
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "anstyle-wincon"
-version = "3.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
-dependencies = [
- "anstyle",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "argon2"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
-dependencies = [
- "base64ct",
- "blake2",
- "cpufeatures",
- "password-hash",
-]
-
-[[package]]
-name = "atomic-waker"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
-
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
-
-[[package]]
-name = "backtrace"
-version = "0.3.73"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
-dependencies = [
- "addr2line",
- "cc",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
-]
-
-[[package]]
-name = "base64"
-version = "0.21.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
-
-[[package]]
-name = "base64"
-version = "0.22.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
-
-[[package]]
-name = "base64ct"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
-
-[[package]]
-name = "base64urlsafedata"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a56894edf5cd1efa7068d7454adeb7ce0b3da4ffa5ab08cfc06165bbc62f0c7"
-dependencies = [
- "base64 0.21.7",
- "paste",
- "serde",
-]
-
-[[package]]
-name = "bindgen"
-version = "0.69.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
-dependencies = [
- "bitflags 2.6.0",
- "cexpr",
- "clang-sys",
- "itertools",
- "lazy_static",
- "lazycell",
- "log",
- "prettyplease",
- "proc-macro2",
- "quote",
- "regex",
- "rustc-hash",
- "shlex",
- "syn 2.0.72",
- "which",
-]
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "bitflags"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
-
-[[package]]
-name = "blake2"
-version = "0.10.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
-dependencies = [
- "digest",
-]
-
-[[package]]
-name = "block-buffer"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "bumpalo"
-version = "3.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
-
-[[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
-[[package]]
-name = "bytes"
-version = "1.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
-
-[[package]]
-name = "cbindgen"
-version = "0.26.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49"
-dependencies = [
- "clap 3.2.25",
- "heck",
- "indexmap 1.9.3",
- "log",
- "proc-macro2",
- "quote",
- "serde",
- "serde_json",
- "syn 1.0.109",
- "tempfile",
- "toml",
-]
-
-[[package]]
-name = "cc"
-version = "1.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549"
-
-[[package]]
-name = "cexpr"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
-dependencies = [
- "nom",
-]
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "chelps"
-version = "4.21.0"
-
-[[package]]
-name = "clang-sys"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
-dependencies = [
- "glob",
- "libc",
- "libloading",
-]
-
-[[package]]
-name = "clap"
-version = "3.2.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
-dependencies = [
- "atty",
- "bitflags 1.3.2",
- "clap_lex 0.2.4",
- "indexmap 1.9.3",
- "strsim 0.10.0",
- "termcolor",
- "textwrap",
-]
-
-[[package]]
-name = "clap"
-version = "4.5.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36"
-dependencies = [
- "clap_builder",
-]
-
-[[package]]
-name = "clap_builder"
-version = "4.5.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed"
-dependencies = [
- "anstream",
- "anstyle",
- "clap_lex 0.7.2",
- "strsim 0.11.1",
-]
-
-[[package]]
-name = "clap_lex"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
-dependencies = [
- "os_str_bytes",
-]
-
-[[package]]
-name = "clap_lex"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
-
-[[package]]
-name = "colorchoice"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
-
-[[package]]
-name = "compact_jwt"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b6493b1c78b7c33fbbb00d8d60d633439fd0c0e44826fb4834efc28121ef266"
-dependencies = [
- "base64 0.21.7",
- "base64urlsafedata",
- "hex",
- "kanidm-hsm-crypto",
- "openssl",
- "openssl-kdf",
- "serde",
- "serde_json",
- "tracing",
- "url",
- "uuid",
-]
-
-[[package]]
-name = "config"
-version = "4.21.0"
-dependencies = [
- "bindgen",
- "libc",
-]
-
-[[package]]
-name = "cookie"
-version = "0.18.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
-dependencies = [
- "percent-encoding",
- "time",
- "version_check",
-]
-
-[[package]]
-name = "cookie_store"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4934e6b7e8419148b6ef56950d277af8561060b56afd59e2aadf98b59fce6baa"
-dependencies = [
- "cookie",
- "idna 0.5.0",
- "log",
- "publicsuffix",
- "serde",
- "serde_derive",
- "serde_json",
- "time",
- "url",
-]
-
-[[package]]
-name = "core-foundation"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "core-foundation-sys"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
-
-[[package]]
-name = "cpufeatures"
-version = "0.2.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "crypto-common"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
-dependencies = [
- "generic-array",
- "typenum",
-]
-
-[[package]]
-name = "cssparser"
-version = "0.31.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be"
-dependencies = [
- "cssparser-macros",
- "dtoa-short",
- "itoa",
- "phf 0.11.2",
- "smallvec",
-]
-
-[[package]]
-name = "cssparser-macros"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
-dependencies = [
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "dbg"
-version = "4.21.0"
-dependencies = [
- "bindgen",
- "chelps",
- "paste",
- "tempfile",
-]
-
-[[package]]
-name = "deranged"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
-dependencies = [
- "powerfmt",
-]
-
-[[package]]
-name = "derive_more"
-version = "0.99.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "digest"
-version = "0.10.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
-dependencies = [
- "block-buffer",
- "crypto-common",
- "subtle",
-]
-
-[[package]]
-name = "dtoa"
-version = "1.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
-
-[[package]]
-name = "dtoa-short"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
-dependencies = [
- "dtoa",
-]
-
-[[package]]
-name = "ego-tree"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591"
-
-[[package]]
-name = "either"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
-
-[[package]]
-name = "encoding_rs"
-version = "0.8.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "equivalent"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
-
-[[package]]
-name = "errno"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
-dependencies = [
- "libc",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "fastrand"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "foreign-types"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
-dependencies = [
- "foreign-types-shared",
-]
-
-[[package]]
-name = "foreign-types-shared"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
-
-[[package]]
-name = "form_urlencoded"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
-dependencies = [
- "percent-encoding",
-]
-
-[[package]]
-name = "futf"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
-dependencies = [
- "mac",
- "new_debug_unreachable",
-]
-
-[[package]]
-name = "futures"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-channel"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
-dependencies = [
- "futures-core",
- "futures-sink",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
-
-[[package]]
-name = "futures-executor"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
-dependencies = [
- "futures-core",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-io"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
-
-[[package]]
-name = "futures-macro"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "futures-sink"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
-
-[[package]]
-name = "futures-task"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
-
-[[package]]
-name = "futures-util"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-macro",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "pin-utils",
- "slab",
-]
-
-[[package]]
-name = "fxhash"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
-dependencies = [
- "byteorder",
-]
-
-[[package]]
-name = "generic-array"
-version = "0.14.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
-dependencies = [
- "typenum",
- "version_check",
-]
-
-[[package]]
-name = "getopts"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
-dependencies = [
- "unicode-width",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
-]
-
-[[package]]
-name = "gimli"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
-
-[[package]]
-name = "glob"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
-
-[[package]]
-name = "h2"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
-dependencies = [
- "atomic-waker",
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "http",
- "indexmap 2.3.0",
- "slab",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
-[[package]]
-name = "hashbrown"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
-[[package]]
-name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
-
-[[package]]
-name = "hex"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
-
-[[package]]
-name = "himmelblaud"
-version = "4.21.0"
-dependencies = [
- "bytes",
- "chelps",
- "clap 4.5.14",
- "dbg",
- "futures",
- "idmap",
- "kanidm-hsm-crypto",
- "libc",
- "libhimmelblau",
- "ntstatus_gen",
- "param",
- "serde",
- "serde_json",
- "sock",
- "talloc",
- "tdb",
- "tempfile",
- "tokio",
- "tokio-util",
- "version",
-]
-
-[[package]]
-name = "home"
-version = "0.5.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
-dependencies = [
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "hostname"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba"
-dependencies = [
- "cfg-if",
- "libc",
- "windows",
-]
-
-[[package]]
-name = "html5ever"
-version = "0.27.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4"
-dependencies = [
- "log",
- "mac",
- "markup5ever",
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "http"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http-body"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
-dependencies = [
- "bytes",
- "http",
-]
-
-[[package]]
-name = "http-body-util"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
-dependencies = [
- "bytes",
- "futures-util",
- "http",
- "http-body",
- "pin-project-lite",
-]
-
-[[package]]
-name = "httparse"
-version = "1.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
-
-[[package]]
-name = "hyper"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-util",
- "h2",
- "http",
- "http-body",
- "httparse",
- "itoa",
- "pin-project-lite",
- "smallvec",
- "tokio",
- "want",
-]
-
-[[package]]
-name = "hyper-rustls"
-version = "0.27.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
-dependencies = [
- "futures-util",
- "http",
- "hyper",
- "hyper-util",
- "rustls",
- "rustls-pki-types",
- "tokio",
- "tokio-rustls",
- "tower-service",
-]
-
-[[package]]
-name = "hyper-tls"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
-dependencies = [
- "bytes",
- "http-body-util",
- "hyper",
- "hyper-util",
- "native-tls",
- "tokio",
- "tokio-native-tls",
- "tower-service",
-]
-
-[[package]]
-name = "hyper-util"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-util",
- "http",
- "http-body",
- "hyper",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "idmap"
-version = "4.21.0"
-dependencies = [
- "bindgen",
- "cc",
- "chelps",
- "dbg",
- "libc",
-]
-
-[[package]]
-name = "idna"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
-dependencies = [
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
-name = "idna"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
-dependencies = [
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
-name = "indexmap"
-version = "1.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
-dependencies = [
- "autocfg",
- "hashbrown 0.12.3",
-]
-
-[[package]]
-name = "indexmap"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
-dependencies = [
- "equivalent",
- "hashbrown 0.14.5",
-]
-
-[[package]]
-name = "ipnet"
-version = "2.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
-
-[[package]]
-name = "is_terminal_polyfill"
-version = "1.70.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
-
-[[package]]
-name = "itertools"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
-dependencies = [
- "either",
-]
-
-[[package]]
-name = "itoa"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
-
-[[package]]
-name = "js-sys"
-version = "0.3.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
-dependencies = [
- "wasm-bindgen",
-]
-
-[[package]]
-name = "kanidm-hsm-crypto"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10b3ed8e86cda3da4f274c677a3057d567bd7b715a0feb06a656e55cc75faf5e"
-dependencies = [
- "argon2",
- "hex",
- "openssl",
- "serde",
- "tracing",
- "zeroize",
-]
-
-[[package]]
-name = "lazy_static"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
-
-[[package]]
-name = "lazycell"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
-
-[[package]]
-name = "libc"
-version = "0.2.155"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
-
-[[package]]
-name = "libhimmelblau"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9bf69727a5d6f3e1e3685569791a7b591e23f811aa6dea5362b274516ee5696"
-dependencies = [
- "base64 0.22.1",
- "cbindgen",
- "compact_jwt",
- "hostname",
- "kanidm-hsm-crypto",
- "openssl",
- "os-release",
- "paste",
- "regex",
- "reqwest",
- "scraper",
- "serde",
- "serde_json",
- "tokio",
- "tracing",
- "tracing-subscriber",
- "urlencoding",
- "uuid",
- "zeroize",
-]
-
-[[package]]
-name = "libloading"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
-dependencies = [
- "cfg-if",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "libnss"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c4bc0291fc787d67c56e9ed79b80780e53bfb9be173177f301ee666cec1021b"
-dependencies = [
- "lazy_static",
- "libc",
- "paste",
-]
-
-[[package]]
-name = "linux-raw-sys"
-version = "0.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
-
-[[package]]
-name = "lock_api"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
-[[package]]
-name = "log"
-version = "0.4.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
-
-[[package]]
-name = "mac"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
-
-[[package]]
-name = "markup5ever"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45"
-dependencies = [
- "log",
- "phf 0.11.2",
- "phf_codegen 0.11.2",
- "string_cache",
- "string_cache_codegen",
- "tendril",
-]
-
-[[package]]
-name = "memchr"
-version = "2.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
-
-[[package]]
-name = "mime"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
-
-[[package]]
-name = "minimal-lexical"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
-
-[[package]]
-name = "miniz_oxide"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
-dependencies = [
- "adler",
-]
-
-[[package]]
-name = "mio"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
-dependencies = [
- "hermit-abi 0.3.9",
- "libc",
- "wasi",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "native-tls"
-version = "0.2.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
-dependencies = [
- "libc",
- "log",
- "openssl",
- "openssl-probe",
- "openssl-sys",
- "schannel",
- "security-framework",
- "security-framework-sys",
- "tempfile",
-]
-
-[[package]]
-name = "new_debug_unreachable"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
-
-[[package]]
-name = "nom"
-version = "7.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
-dependencies = [
- "memchr",
- "minimal-lexical",
-]
-
-[[package]]
-name = "nss_himmelblau"
-version = "4.21.0"
-dependencies = [
- "libc",
- "libnss",
- "ntstatus_gen",
- "param",
- "sock",
- "version",
-]
-
-[[package]]
-name = "ntstatus_gen"
-version = "4.21.0"
-
-[[package]]
-name = "nu-ansi-term"
-version = "0.46.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
-dependencies = [
- "overload",
- "winapi",
-]
-
-[[package]]
-name = "num-conv"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
-
-[[package]]
-name = "object"
-version = "0.36.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
-
-[[package]]
-name = "openssl"
-version = "0.10.66"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
-dependencies = [
- "bitflags 2.6.0",
- "cfg-if",
- "foreign-types",
- "libc",
- "once_cell",
- "openssl-macros",
- "openssl-sys",
-]
-
-[[package]]
-name = "openssl-kdf"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "feda4b74ded511c6d8543db00166f70f572ca7e5eb72f4817bf08628b5bbc1f4"
-dependencies = [
- "foreign-types",
- "libc",
- "openssl",
- "openssl-sys",
- "pkg-config",
- "thiserror",
-]
-
-[[package]]
-name = "openssl-macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "openssl-probe"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
-
-[[package]]
-name = "openssl-sys"
-version = "0.9.103"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
-dependencies = [
- "cc",
- "libc",
- "pkg-config",
- "vcpkg",
-]
-
-[[package]]
-name = "os-release"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82f29ae2f71b53ec19cc23385f8e4f3d90975195aa3d09171ba3bef7159bec27"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
-name = "os_str_bytes"
-version = "6.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
-
-[[package]]
-name = "overload"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
-
-[[package]]
-name = "pam_himmelblau"
-version = "4.21.0"
-dependencies = [
- "chelps",
- "dbg",
- "libc",
- "param",
- "pkg-config",
- "sock",
-]
-
-[[package]]
-name = "param"
-version = "4.21.0"
-dependencies = [
- "bindgen",
- "chelps",
- "dbg",
- "ntstatus_gen",
- "paste",
-]
-
-[[package]]
-name = "parking_lot"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "password-hash"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
-dependencies = [
- "base64ct",
- "rand_core",
- "subtle",
-]
-
-[[package]]
-name = "paste"
-version = "1.0.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
-
-[[package]]
-name = "percent-encoding"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
-
-[[package]]
-name = "phf"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
-dependencies = [
- "phf_shared 0.10.0",
-]
-
-[[package]]
-name = "phf"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
-dependencies = [
- "phf_macros",
- "phf_shared 0.11.2",
-]
-
-[[package]]
-name = "phf_codegen"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
-dependencies = [
- "phf_generator 0.10.0",
- "phf_shared 0.10.0",
-]
-
-[[package]]
-name = "phf_codegen"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
-dependencies = [
- "phf_generator 0.11.2",
- "phf_shared 0.11.2",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
-dependencies = [
- "phf_shared 0.10.0",
- "rand",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
-dependencies = [
- "phf_shared 0.11.2",
- "rand",
-]
-
-[[package]]
-name = "phf_macros"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
-dependencies = [
- "phf_generator 0.11.2",
- "phf_shared 0.11.2",
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
-dependencies = [
- "siphasher",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
-dependencies = [
- "siphasher",
-]
-
-[[package]]
-name = "pin-project"
-version = "1.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
-dependencies = [
- "pin-project-internal",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "1.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "pin-project-lite"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
-
-[[package]]
-name = "pin-utils"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
-
-[[package]]
-name = "pkg-config"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
-
-[[package]]
-name = "powerfmt"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
-dependencies = [
- "zerocopy",
-]
-
-[[package]]
-name = "precomputed-hash"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
-
-[[package]]
-name = "prettyplease"
-version = "0.2.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
-dependencies = [
- "proc-macro2",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.86"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "psl-types"
-version = "2.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
-
-[[package]]
-name = "publicsuffix"
-version = "2.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457"
-dependencies = [
- "idna 0.3.0",
- "psl-types",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
-dependencies = [
- "bitflags 2.6.0",
-]
-
-[[package]]
-name = "regex"
-version = "1.10.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
-
-[[package]]
-name = "reqwest"
-version = "0.12.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
-dependencies = [
- "base64 0.22.1",
- "bytes",
- "cookie",
- "cookie_store",
- "encoding_rs",
- "futures-core",
- "futures-util",
- "h2",
- "http",
- "http-body",
- "http-body-util",
- "hyper",
- "hyper-rustls",
- "hyper-tls",
- "hyper-util",
- "ipnet",
- "js-sys",
- "log",
- "mime",
- "native-tls",
- "once_cell",
- "percent-encoding",
- "pin-project-lite",
- "rustls-pemfile",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "sync_wrapper",
- "system-configuration",
- "tokio",
- "tokio-native-tls",
- "tower-service",
- "url",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "web-sys",
- "winreg",
-]
-
-[[package]]
-name = "ring"
-version = "0.17.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
-dependencies = [
- "cc",
- "cfg-if",
- "getrandom",
- "libc",
- "spin",
- "untrusted",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "rustc-demangle"
-version = "0.1.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
-
-[[package]]
-name = "rustc-hash"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
-
-[[package]]
-name = "rustix"
-version = "0.38.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
-dependencies = [
- "bitflags 2.6.0",
- "errno",
- "libc",
- "linux-raw-sys",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "rustls"
-version = "0.23.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
-dependencies = [
- "once_cell",
- "rustls-pki-types",
- "rustls-webpki",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "rustls-pemfile"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
-dependencies = [
- "base64 0.22.1",
- "rustls-pki-types",
-]
-
-[[package]]
-name = "rustls-pki-types"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
-
-[[package]]
-name = "rustls-webpki"
-version = "0.102.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
-dependencies = [
- "ring",
- "rustls-pki-types",
- "untrusted",
-]
-
-[[package]]
-name = "ryu"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
-
-[[package]]
-name = "schannel"
-version = "0.1.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
-dependencies = [
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
-[[package]]
-name = "scraper"
-version = "0.19.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "761fb705fdf625482d2ed91d3f0559dcfeab2798fe2771c69560a774865d0802"
-dependencies = [
- "ahash",
- "cssparser",
- "ego-tree",
- "getopts",
- "html5ever",
- "once_cell",
- "selectors",
- "tendril",
-]
-
-[[package]]
-name = "security-framework"
-version = "2.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
-dependencies = [
- "bitflags 2.6.0",
- "core-foundation",
- "core-foundation-sys",
- "libc",
- "security-framework-sys",
-]
-
-[[package]]
-name = "security-framework-sys"
-version = "2.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "selectors"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06"
-dependencies = [
- "bitflags 2.6.0",
- "cssparser",
- "derive_more",
- "fxhash",
- "log",
- "new_debug_unreachable",
- "phf 0.10.1",
- "phf_codegen 0.10.0",
- "precomputed-hash",
- "servo_arc",
- "smallvec",
-]
-
-[[package]]
-name = "serde"
-version = "1.0.205"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.205"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.122"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
-dependencies = [
- "itoa",
- "memchr",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "serde_urlencoded"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
-dependencies = [
- "form_urlencoded",
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "servo_arc"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44"
-dependencies = [
- "stable_deref_trait",
-]
-
-[[package]]
-name = "sharded-slab"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
-name = "shlex"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
-
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "siphasher"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
-
-[[package]]
-name = "slab"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "smallvec"
-version = "1.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
-
-[[package]]
-name = "sock"
-version = "4.21.0"
-dependencies = [
- "chelps",
- "dbg",
- "libc",
- "libnss",
- "ntstatus_gen",
- "param",
- "serde",
- "serde_json",
-]
-
-[[package]]
-name = "socket2"
-version = "0.5.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
-dependencies = [
- "libc",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "spin"
-version = "0.9.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
-
-[[package]]
-name = "stable_deref_trait"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
-
-[[package]]
-name = "string_cache"
-version = "0.8.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
-dependencies = [
- "new_debug_unreachable",
- "once_cell",
- "parking_lot",
- "phf_shared 0.10.0",
- "precomputed-hash",
- "serde",
-]
-
-[[package]]
-name = "string_cache_codegen"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
-dependencies = [
- "phf_generator 0.10.0",
- "phf_shared 0.10.0",
- "proc-macro2",
- "quote",
-]
-
-[[package]]
-name = "strsim"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
-
-[[package]]
-name = "strsim"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
-
-[[package]]
-name = "subtle"
-version = "2.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
-
-[[package]]
-name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.72"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "sync_wrapper"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
-
-[[package]]
-name = "system-configuration"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
-dependencies = [
- "bitflags 1.3.2",
- "core-foundation",
- "system-configuration-sys",
-]
-
-[[package]]
-name = "system-configuration-sys"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "talloc"
-version = "4.21.0"
-dependencies = [
- "bindgen",
- "chelps",
- "config",
-]
-
-[[package]]
-name = "tdb"
-version = "4.21.0"
-dependencies = [
- "bindgen",
- "chelps",
- "config",
- "dbg",
- "libc",
- "ntstatus_gen",
-]
-
-[[package]]
-name = "tempfile"
-version = "3.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
-dependencies = [
- "cfg-if",
- "fastrand",
- "once_cell",
- "rustix",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "tendril"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
-dependencies = [
- "futf",
- "mac",
- "utf-8",
-]
-
-[[package]]
-name = "termcolor"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.16.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
-
-[[package]]
-name = "thiserror"
-version = "1.0.63"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
-dependencies = [
- "thiserror-impl",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.63"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "thread_local"
-version = "1.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
-dependencies = [
- "cfg-if",
- "once_cell",
-]
-
-[[package]]
-name = "time"
-version = "0.3.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
-dependencies = [
- "deranged",
- "itoa",
- "num-conv",
- "powerfmt",
- "serde",
- "time-core",
- "time-macros",
-]
-
-[[package]]
-name = "time-core"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
-
-[[package]]
-name = "time-macros"
-version = "0.2.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
-dependencies = [
- "num-conv",
- "time-core",
-]
-
-[[package]]
-name = "tinyvec"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
-dependencies = [
- "tinyvec_macros",
-]
-
-[[package]]
-name = "tinyvec_macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-
-[[package]]
-name = "tokio"
-version = "1.39.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
-dependencies = [
- "backtrace",
- "bytes",
- "libc",
- "mio",
- "parking_lot",
- "pin-project-lite",
- "signal-hook-registry",
- "socket2",
- "tokio-macros",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "tokio-native-tls"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
-dependencies = [
- "native-tls",
- "tokio",
-]
-
-[[package]]
-name = "tokio-rustls"
-version = "0.26.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
-dependencies = [
- "rustls",
- "rustls-pki-types",
- "tokio",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.7.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "toml"
-version = "0.5.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "tower"
-version = "0.4.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
-dependencies = [
- "futures-core",
- "futures-util",
- "pin-project",
- "pin-project-lite",
- "tokio",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "tower-layer"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
-
-[[package]]
-name = "tower-service"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
-
-[[package]]
-name = "tracing"
-version = "0.1.40"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
-dependencies = [
- "pin-project-lite",
- "tracing-attributes",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-attributes"
-version = "0.1.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
-dependencies = [
- "once_cell",
- "valuable",
-]
-
-[[package]]
-name = "tracing-log"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
-dependencies = [
- "log",
- "once_cell",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-subscriber"
-version = "0.3.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
-dependencies = [
- "nu-ansi-term",
- "sharded-slab",
- "smallvec",
- "thread_local",
- "tracing-core",
- "tracing-log",
-]
-
-[[package]]
-name = "try-lock"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
-
-[[package]]
-name = "typenum"
-version = "1.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
-
-[[package]]
-name = "unicode-bidi"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
-
-[[package]]
-name = "unicode-normalization"
-version = "0.1.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
-dependencies = [
- "tinyvec",
-]
-
-[[package]]
-name = "unicode-width"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
-
-[[package]]
-name = "untrusted"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
-
-[[package]]
-name = "url"
-version = "2.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
-dependencies = [
- "form_urlencoded",
- "idna 0.5.0",
- "percent-encoding",
- "serde",
-]
-
-[[package]]
-name = "urlencoding"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
-
-[[package]]
-name = "utf-8"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
-
-[[package]]
-name = "utf8parse"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
-
-[[package]]
-name = "uuid"
-version = "1.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
-dependencies = [
- "getrandom",
- "serde",
-]
-
-[[package]]
-name = "valuable"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
-
-[[package]]
-name = "vcpkg"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
-
-[[package]]
-name = "version"
-version = "4.21.0"
-dependencies = [
- "bindgen",
- "cc",
- "chelps",
- "libc",
-]
-
-[[package]]
-name = "version_check"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-
-[[package]]
-name = "want"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
-dependencies = [
- "try-lock",
-]
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
-dependencies = [
- "cfg-if",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
-dependencies = [
- "bumpalo",
- "log",
- "once_cell",
- "proc-macro2",
- "quote",
- "syn 2.0.72",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-futures"
-version = "0.4.42"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
-dependencies = [
- "cfg-if",
- "js-sys",
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
-
-[[package]]
-name = "web-sys"
-version = "0.3.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "which"
-version = "4.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
-dependencies = [
- "either",
- "home",
- "once_cell",
- "rustix",
-]
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-util"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
-dependencies = [
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "windows"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
-dependencies = [
- "windows-core",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-core"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
-dependencies = [
- "windows-targets 0.48.5",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
-dependencies = [
- "windows_aarch64_gnullvm 0.48.5",
- "windows_aarch64_msvc 0.48.5",
- "windows_i686_gnu 0.48.5",
- "windows_i686_msvc 0.48.5",
- "windows_x86_64_gnu 0.48.5",
- "windows_x86_64_gnullvm 0.48.5",
- "windows_x86_64_msvc 0.48.5",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
-dependencies = [
- "windows_aarch64_gnullvm 0.52.6",
- "windows_aarch64_msvc 0.52.6",
- "windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm",
- "windows_i686_msvc 0.52.6",
- "windows_x86_64_gnu 0.52.6",
- "windows_x86_64_gnullvm 0.52.6",
- "windows_x86_64_msvc 0.52.6",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
-
-[[package]]
-name = "winreg"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
-dependencies = [
- "cfg-if",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "zerocopy"
-version = "0.7.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
-dependencies = [
- "byteorder",
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.7.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
-
-[[package]]
-name = "zeroize"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
-dependencies = [
- "zeroize_derive",
-]
-
-[[package]]
-name = "zeroize_derive"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.72",
-]
[workspace]
resolver = "2"
members = [
- "chelps", "config", "dbg", "himmelblaud", "idmap",
- "nss", "ntstatus_gen", "pam",
- "param", "sock", "talloc", "tdb", "version",
]
[workspace.dependencies]
-param = { path = "param" }
-dbg = { path = "dbg" }
-chelps = { path = "chelps" }
-sock = { path = "sock" }
-ntstatus_gen = { path = "ntstatus_gen" }
-tdb = { path = "tdb" }
-idmap = { path = "idmap" }
-libc = "0.2.155"
-config = { path = "config" }
+++ /dev/null
-use std::env;
-
-fn main() {
- if let Some(vers) = version::samba_version_string() {
- println!("cargo:rustc-env=CARGO_PKG_VERSION={}", vers);
- }
- println!(
- "cargo:rustc-env=CARGO_PKG_VERSION_MAJOR={}",
- version::SAMBA_VERSION_MAJOR
- );
- println!(
- "cargo:rustc-env=CARGO_PKG_VERSION_MINOR={}",
- version::SAMBA_VERSION_MINOR
- );
- println!(
- "cargo:rustc-env=CARGO_PKG_VERSION_PATCH={}",
- version::SAMBA_VERSION_RELEASE
- );
-}
+++ /dev/null
-[package]
-name = "chelps"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[dependencies]
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- C conversion helper functions and macros
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-use std::ffi::{CStr, CString};
-use std::os::raw::c_char;
-use std::ptr;
-
-pub unsafe fn wrap_c_char(input: *const c_char) -> Option<String> {
- if input.is_null() {
- return None;
- }
-
- let c_str = unsafe { CStr::from_ptr(input) };
- match c_str.to_str() {
- Ok(output) => Some(output.to_string()),
- Err(_) => None,
- }
-}
-
-pub fn wrap_string(input: &str) -> *mut c_char {
- match CString::new(input.to_string()) {
- Ok(msg) => msg.into_raw(),
- Err(_) => ptr::null_mut(),
- }
-}
-
-pub unsafe fn string_free(input: *mut c_char) {
- if !input.is_null() {
- unsafe {
- let _ = CString::from_raw(input);
- }
- }
-}
-
-#[macro_export]
-macro_rules! function {
- () => {{
- fn f() {}
- fn type_name_of<T>(_: T) -> &'static str {
- std::any::type_name::<T>()
- }
- let name = type_name_of(f);
-
- let base_name = match name.rfind("::") {
- Some(pos) => &name[..pos],
- None => name,
- };
- let parts: Vec<&str> = base_name
- .split("::")
- .filter(|&p| p != "{{closure}}")
- .collect();
- parts.join("::")
- }};
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::ffi::CString;
- use std::ptr;
-
- #[test]
- fn test_wrap_c_char_non_null() {
- let original = "Hello, world!";
- let c_string = CString::new(original).expect("CString::new failed");
- let c_ptr = c_string.as_ptr();
-
- let result = unsafe { wrap_c_char(c_ptr) };
- assert_eq!(result, Some(original.to_string()));
- }
-
- #[test]
- fn test_wrap_c_char_null() {
- let result = unsafe { wrap_c_char(ptr::null()) };
- assert!(result.is_none());
- }
-
- #[test]
- fn test_wrap_c_char_invalid_utf8() {
- let invalid_utf8 = vec![0xff, 0xff, 0xff, 0xff];
- let c_string = CString::new(invalid_utf8).expect("CString::new failed");
- let c_ptr = c_string.as_ptr();
-
- let result = unsafe { wrap_c_char(c_ptr) };
- assert!(result.is_none());
- }
-
- #[test]
- fn test_wrap_string() {
- let original = "Hello, world!";
- let c_ptr = wrap_string(original);
-
- let c_str = unsafe { CStr::from_ptr(c_ptr) };
- let result = c_str.to_str().expect("CStr::to_str failed");
-
- assert_eq!(result, original);
-
- // Clean up the allocated memory
- unsafe { string_free(c_ptr) };
- }
-
- #[test]
- fn test_wrap_string_empty() {
- let original = "";
- let c_ptr = wrap_string(original);
-
- let c_str = unsafe { CStr::from_ptr(c_ptr) };
- let result = c_str.to_str().expect("CStr::to_str failed");
-
- assert_eq!(result, original);
-
- // Clean up the allocated memory
- unsafe { string_free(c_ptr) };
- }
-
- #[test]
- fn test_wrap_string_null_pointer() {
- let c_ptr = wrap_string("\0");
- assert!(c_ptr.is_null());
- }
-
- #[test]
- fn test_string_free_null() {
- unsafe { string_free(ptr::null_mut()) };
- // No assertion needed, just ensuring no crash occurs
- }
-
- #[test]
- fn test_string_free_non_null() {
- let original = "Hello, world!";
- let c_ptr = wrap_string(original);
-
- unsafe { string_free(c_ptr) };
- // No assertion needed, just ensuring the memory was freed without a crash
- }
-}
+++ /dev/null
-[package]
-name = "config"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[dependencies]
-libc.workspace = true
-
-[build-dependencies]
-bindgen = "0.69.4"
+++ /dev/null
-#include "../../bin/default/include/config.h"
-#ifndef USING_SYSTEM_TDB
-#define USING_SYSTEM_TDB 0
-#endif
-
-#ifndef USING_SYSTEM_TALLOC
-#define USING_SYSTEM_TALLOC 0
-#endif
+++ /dev/null
-use std::env;
-use std::path::PathBuf;
-
-fn main() {
- let header = "../../bin/default/include/config.h";
- println!("cargo:rerun-if-changed={}", header);
- let additions_header = "./additions.h";
- println!("cargo:rerun-if-changed={}", additions_header);
-
- let bindings = bindgen::Builder::default()
- .header(additions_header)
- .header(header)
- .generate()
- .expect("Failed generating config bindings!");
-
- let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
- bindings
- .write_to_file(out_path.join("bindings.rs"))
- .expect("Couldn't write bindings!");
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Samba config imported into Rust
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#![allow(non_upper_case_globals)]
-#![allow(non_camel_case_types)]
-#![allow(non_snake_case)]
-#![allow(dead_code)]
-#![allow(clippy::upper_case_acronyms)]
-include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_using_system_tdb() {
- // This test just ensures that USING_SYSTEM_TDB is available from the
- // config. None of the other options are really used at the moment.
- assert!(
- USING_SYSTEM_TDB == 0 || USING_SYSTEM_TDB == 1,
- "Unexpected value for USING_SYSTEM_TDB: {}",
- USING_SYSTEM_TDB
- );
- }
-
- #[test]
- fn test_using_system_talloc() {
- assert!(
- USING_SYSTEM_TALLOC == 0 || USING_SYSTEM_TALLOC == 1,
- "Unexpected value for USING_SYSTEM_TALLOC: {}",
- USING_SYSTEM_TALLOC
- );
- }
-}
+++ /dev/null
-[package]
-name = "dbg"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[build-dependencies]
-bindgen = "0.69.4"
-
-[dependencies]
-chelps.workspace = true
-
-[dev-dependencies]
-tempfile = "3.12.0"
-paste = "1.0.15"
+++ /dev/null
-use std::env;
-use std::path::PathBuf;
-
-fn main() {
- let bindings = bindgen::Builder::default()
- .header("../../lib/util/debug.h")
- .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
- .generate()
- .expect("Unable to generate bindings");
-
- let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
- bindings
- .write_to_file(out_path.join("bindings.rs"))
- .expect("Couldn't write bindings!");
-
- let mut src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
- src_dir.push("../../bin/default/lib/util");
- println!(
- "cargo:rustc-link-search=native={}",
- src_dir.to_str().unwrap()
- );
- println!("cargo:rustc-link-lib=samba-debug-private-samba");
- println!("cargo:rustc-link-lib=samba-util");
- println!("cargo:rustc-env=LD_LIBRARY_PATH=../../bin/shared:../../bin/shared/private/");
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Parameter loading functions
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-pub mod ffi {
- #![allow(non_upper_case_globals)]
- #![allow(non_camel_case_types)]
- #![allow(non_snake_case)]
- #![allow(dead_code)]
- #![allow(clippy::upper_case_acronyms)]
- include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-}
-
-pub const MAX_DEBUG_LEVEL: u32 = ffi::MAX_DEBUG_LEVEL;
-pub const DBGLVL_ERR: u32 = ffi::DBGLVL_ERR;
-pub const DBGLVL_WARNING: u32 = ffi::DBGLVL_WARNING;
-pub const DBGLVL_NOTICE: u32 = ffi::DBGLVL_NOTICE;
-pub const DBGLVL_INFO: u32 = ffi::DBGLVL_INFO;
-pub const DBGLVL_DEBUG: u32 = ffi::DBGLVL_DEBUG;
-
-pub const DEBUG_DEFAULT_STDERR: ffi::debug_logtype =
- ffi::debug_logtype_DEBUG_DEFAULT_STDERR;
-pub const DEBUG_DEFAULT_STDOUT: ffi::debug_logtype =
- ffi::debug_logtype_DEBUG_DEFAULT_STDOUT;
-pub const DEBUG_FILE: ffi::debug_logtype = ffi::debug_logtype_DEBUG_FILE;
-pub const DEBUG_STDOUT: ffi::debug_logtype = ffi::debug_logtype_DEBUG_STDOUT;
-pub const DEBUG_STDERR: ffi::debug_logtype = ffi::debug_logtype_DEBUG_STDERR;
-pub const DEBUG_CALLBACK: ffi::debug_logtype =
- ffi::debug_logtype_DEBUG_CALLBACK;
-
-pub fn debug_set_logfile(name: &str) {
- let name_cstr = chelps::wrap_string(name);
- unsafe {
- ffi::debug_set_logfile(name_cstr);
- chelps::string_free(name_cstr);
- }
-}
-
-pub fn setup_logging(prog_name: &str, new_logtype: ffi::debug_logtype) {
- let prog_name_cstr = chelps::wrap_string(prog_name);
- unsafe {
- ffi::setup_logging(prog_name_cstr, new_logtype);
- chelps::string_free(prog_name_cstr);
- }
-}
-
-pub fn dbgflush() {
- unsafe {
- ffi::dbgflush();
- }
-}
-
-#[macro_export]
-macro_rules! debuglevel_set {
- ($level:expr) => {{
- unsafe {
- $crate::ffi::debuglevel_set_class(
- $crate::ffi::DBGC_ALL as usize,
- $level as i32,
- )
- }
- }};
-}
-
-#[macro_export]
-macro_rules! DBG_PREFIX {
- ($level:expr $(, $arg:expr)* $(,)?) => {{
- if $level <= $crate::ffi::MAX_DEBUG_LEVEL {
- let location = format!("{}:{}", file!(), line!());
- let location_cstr = chelps::wrap_string(&location);
- let function = chelps::function!();
- let function_msg = format!("{}: ", function);
- let function_cstr = chelps::wrap_string(&function);
- let function_msg_cstr = chelps::wrap_string(&function_msg);
- // Always append a newline to the debug, otherwise it won't flush
- // to the log.
- let msg = format!("{}\n", format!($($arg),*));
- let msg_cstr = chelps::wrap_string(&msg);
- unsafe {
- let _ = $crate::ffi::debuglevel_get_class($crate::ffi::DBGC_CLASS as usize) >= ($level as i32)
- && $crate::ffi::dbghdrclass($level as i32,
- $crate::ffi::DBGC_CLASS as i32,
- location_cstr,
- function_cstr)
- && $crate::ffi::dbgtext(function_msg_cstr)
- && $crate::ffi::dbgtext(msg_cstr);
- chelps::string_free(location_cstr);
- chelps::string_free(function_cstr);
- chelps::string_free(function_msg_cstr);
- chelps::string_free(msg_cstr);
- }
- }
- }}
-}
-
-#[macro_export]
-macro_rules! DBG_ERR {
- ($msg:expr $(, $arg:expr)* $(,)?) => {{
- $crate::DBG_PREFIX!($crate::ffi::DBGLVL_ERR, $msg, $($arg),*)
- }}
-}
-
-#[macro_export]
-macro_rules! DBG_WARNING {
- ($msg:expr $(, $arg:expr)* $(,)?) => {{
- $crate::DBG_PREFIX!($crate::ffi::DBGLVL_WARNING, $msg, $($arg),*)
- }}
-}
-
-#[macro_export]
-macro_rules! DBG_NOTICE {
- ($msg:expr $(, $arg:expr)* $(,)?) => {{
- $crate::DBG_PREFIX!($crate::ffi::DBGLVL_NOTICE, $msg, $($arg),*)
- }}
-}
-
-#[macro_export]
-macro_rules! DBG_INFO {
- ($msg:expr $(, $arg:expr)* $(,)?) => {{
- $crate::DBG_PREFIX!($crate::ffi::DBGLVL_INFO, $msg, $($arg),*)
- }}
-}
-
-#[macro_export]
-macro_rules! DBG_DEBUG {
- ($msg:expr $(, $arg:expr)* $(,)?) => {{
- $crate::DBG_PREFIX!($crate::ffi::DBGLVL_DEBUG, $msg, $($arg),*)
- }}
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use paste::paste;
- use std::fs::File;
- use std::io::Read;
- use tempfile::NamedTempFile;
-
- #[test]
- fn test_debug_constants() {
- assert_eq!(MAX_DEBUG_LEVEL, ffi::MAX_DEBUG_LEVEL);
- assert_eq!(DBGLVL_ERR, ffi::DBGLVL_ERR);
- assert_eq!(DBGLVL_WARNING, ffi::DBGLVL_WARNING);
- assert_eq!(DBGLVL_NOTICE, ffi::DBGLVL_NOTICE);
- assert_eq!(DBGLVL_INFO, ffi::DBGLVL_INFO);
- assert_eq!(DBGLVL_DEBUG, ffi::DBGLVL_DEBUG);
-
- assert_eq!(
- DEBUG_DEFAULT_STDERR,
- ffi::debug_logtype_DEBUG_DEFAULT_STDERR
- );
- assert_eq!(
- DEBUG_DEFAULT_STDOUT,
- ffi::debug_logtype_DEBUG_DEFAULT_STDOUT
- );
- assert_eq!(DEBUG_FILE, ffi::debug_logtype_DEBUG_FILE);
- assert_eq!(DEBUG_STDOUT, ffi::debug_logtype_DEBUG_STDOUT);
- assert_eq!(DEBUG_STDERR, ffi::debug_logtype_DEBUG_STDERR);
- assert_eq!(DEBUG_CALLBACK, ffi::debug_logtype_DEBUG_CALLBACK);
- }
-
- macro_rules! test_dbg_macro {
- ($level:ident) => {
- paste! {
- #[test]
- fn [<test_dbg_ $level:lower _macro>]() {
- let logfile = NamedTempFile::new().expect("Failed to create temporary file");
- let logfile = logfile.path().to_str().unwrap();
- setup_logging("test_program", DEBUG_FILE);
- debug_set_logfile(logfile);
-
- let logfile_output = concat!("This is a ", stringify!($level), " message");
-
- debuglevel_set!([<DBGLVL_ $level:upper>]);
-
- [<DBG_ $level:upper>]!("{}\n", logfile_output);
- dbgflush();
-
- let mut file = File::open(logfile).expect("Failed to open logfile");
- let mut logfile_contents = String::new();
- file.read_to_string(&mut logfile_contents)
- .expect("Failed to read logfile");
- assert!(
- logfile_contents.contains(logfile_output),
- "Test data missing from logfile: {}",
- logfile_contents
- );
- }
- }
- };
- }
-
- test_dbg_macro!(DEBUG);
- // Multiple re-inits of the debug env cause it to fail, so we can't
- // reliably test all of these in one go.
- //test_dbg_macro!(INFO);
- //test_dbg_macro!(NOTICE);
- //test_dbg_macro!(WARNING);
- //test_dbg_macro!(ERR);
-}
+++ /dev/null
-[package]
-name = "himmelblaud"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[dependencies]
-libhimmelblau = "0.2.9"
-ntstatus_gen.workspace = true
-param = { workspace = true }
-sock = { workspace = true }
-tdb = { workspace = true }
-dbg = { workspace = true }
-chelps.workspace = true
-clap = "4.5.9"
-kanidm-hsm-crypto = "0.2.0"
-serde_json = "1.0.120"
-tokio = { version = "1.38.1", features = [ "rt", "macros" ] }
-tokio-util = { version = "0.7.11", features = ["codec"] }
-bytes = "1.6.1"
-futures = "0.3.30"
-serde = "1.0.204"
-idmap = { workspace = true }
-libc = { workspace = true }
-talloc = { version = "4.21.0", path = "../talloc" }
-
-[build-dependencies]
-version = { path = "../version" }
-
-[dev-dependencies]
-tempfile = "3.12.0"
+++ /dev/null
-fn main() {
- println!("cargo:rustc-env=LD_LIBRARY_PATH=../../bin/shared:../../bin/shared/private/");
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon cache
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use dbg::DBG_ERR;
-use himmelblau::error::MsalError;
-use himmelblau::graph::DirectoryObject;
-use himmelblau::UserToken;
-use kanidm_hsm_crypto::{
- AuthValue, BoxedDynTpm, LoadableIdentityKey, LoadableMachineKey,
- LoadableMsOapxbcRsaKey, Tpm,
-};
-use libc::uid_t;
-use ntstatus_gen::*;
-use serde::{Deserialize, Serialize};
-use serde_json::{from_slice as json_from_slice, to_vec as json_to_vec};
-use std::collections::HashSet;
-use tdb::Tdb;
-
-struct BasicCache {
- tdb: Tdb,
-}
-
-impl BasicCache {
- fn new(cache_path: &str) -> Result<Self, Box<NTSTATUS>> {
- let tdb =
- Tdb::open(cache_path, None, None, None, None).map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_FILE_INVALID)
- })?;
- Ok(BasicCache { tdb })
- }
-
- fn fetch_str(&self, key: &str) -> Option<String> {
- let key = key.to_string().to_lowercase();
- let exists = match self.tdb.exists(&key) {
- Ok(exists) => exists,
- Err(e) => {
- DBG_ERR!("Failed to fetch {}: {:?}", key, e);
- false
- }
- };
- if exists {
- match self.tdb.fetch(&key) {
- Ok(val) => Some(val),
- Err(e) => {
- DBG_ERR!("Failed to fetch {}: {:?}", key, e);
- None
- }
- }
- } else {
- None
- }
- }
-
- fn fetch<'a, T>(&self, key: &str) -> Option<T>
- where
- T: for<'de> Deserialize<'de>,
- {
- let key = key.to_string().to_lowercase();
- match self.fetch_str(&key) {
- Some(val) => match json_from_slice::<T>(val.as_bytes()) {
- Ok(res) => Some(res),
- Err(e) => {
- DBG_ERR!("Failed to decode {}: {:?}", key, e);
- None
- }
- },
- None => {
- return None;
- }
- }
- }
-
- fn store_bytes(
- &mut self,
- key: &str,
- val: &[u8],
- ) -> Result<(), Box<NTSTATUS>> {
- let key = key.to_string().to_lowercase();
- match self.tdb.transaction_start() {
- Ok(start) => {
- if !start {
- DBG_ERR!("Failed to start the database transaction.");
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- }
- Err(e) => {
- DBG_ERR!("Failed to start the database transaction: {:?}", e);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- };
-
- let res = match self.tdb.store(&key, val, None) {
- Ok(res) => Some(res),
- Err(e) => {
- DBG_ERR!("Unable to persist {}: {:?}", key, e);
- None
- }
- };
-
- let res = match res {
- Some(res) => res,
- None => {
- let _ = self.tdb.transaction_cancel();
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- };
- if !res {
- DBG_ERR!("Unable to persist {}", key);
- let _ = self.tdb.transaction_cancel();
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
-
- let success = match self.tdb.transaction_commit() {
- Ok(success) => success,
- Err(e) => {
- DBG_ERR!("Failed to commit the database transaction: {:?}", e);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- };
- if !success {
- DBG_ERR!("Failed to commit the database transaction.");
- let _ = self.tdb.transaction_cancel();
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
-
- Ok(())
- }
-
- fn store<T>(&mut self, key: &str, val: T) -> Result<(), Box<NTSTATUS>>
- where
- T: Serialize,
- {
- let key = key.to_string().to_lowercase();
- let val_bytes = match json_to_vec(&val) {
- Ok(val_bytes) => val_bytes,
- Err(e) => {
- DBG_ERR!("Unable to serialize {}: {:?}", key, e);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- };
- self.store_bytes(&key, &val_bytes)
- }
-
- fn keys(&self) -> Result<Vec<String>, Box<NTSTATUS>> {
- self.tdb.keys().map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_UNSUCCESSFUL)
- })
- }
-}
-
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub(crate) struct UserEntry {
- pub(crate) upn: String,
- pub(crate) uuid: String,
- pub(crate) name: String,
-}
-
-impl TryFrom<&UserToken> for UserEntry {
- type Error = MsalError;
-
- fn try_from(token: &UserToken) -> Result<Self, Self::Error> {
- Ok(UserEntry {
- upn: token.spn()?,
- uuid: token.uuid()?.to_string(),
- name: token.id_token.name.clone(),
- })
- }
-}
-
-pub(crate) struct UserCache {
- cache: BasicCache,
-}
-
-impl UserCache {
- pub(crate) fn new(cache_path: &str) -> Result<Self, Box<NTSTATUS>> {
- Ok(UserCache {
- cache: BasicCache::new(cache_path)?,
- })
- }
-
- pub(crate) fn fetch(&mut self, upn: &str) -> Option<UserEntry> {
- self.cache.fetch::<UserEntry>(upn)
- }
-
- pub(crate) fn fetch_all(
- &mut self,
- ) -> Result<Vec<UserEntry>, Box<NTSTATUS>> {
- let keys = self.cache.keys()?;
- let mut res = Vec::new();
- for upn in keys {
- let entry = match self.cache.fetch::<UserEntry>(&upn) {
- Some(entry) => entry,
- None => {
- DBG_ERR!("Unable to fetch user {}", upn);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- };
- res.push(entry);
- }
- Ok(res)
- }
-
- pub(crate) fn store(
- &mut self,
- entry: UserEntry,
- ) -> Result<(), Box<NTSTATUS>> {
- let key = entry.upn.clone();
- self.cache.store::<UserEntry>(&key, entry)
- }
-}
-
-pub(crate) struct UidCache {
- cache: BasicCache,
-}
-
-impl UidCache {
- pub(crate) fn new(cache_path: &str) -> Result<Self, Box<NTSTATUS>> {
- Ok(UidCache {
- cache: BasicCache::new(cache_path)?,
- })
- }
-
- pub(crate) fn store(
- &mut self,
- uid: uid_t,
- upn: &str,
- ) -> Result<(), Box<NTSTATUS>> {
- let key = format!("{}", uid);
- let upn = upn.to_string().to_lowercase();
- self.cache.store_bytes(&key, upn.as_bytes())
- }
-
- pub(crate) fn fetch(&mut self, uid: uid_t) -> Option<String> {
- let key = format!("{}", uid);
- self.cache.fetch_str(&key)
- }
-}
-
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub(crate) struct GroupEntry {
- pub(crate) uuid: String,
- members: HashSet<String>,
-}
-
-impl From<DirectoryObject> for GroupEntry {
- fn from(obj: DirectoryObject) -> Self {
- GroupEntry {
- uuid: obj.id.clone(),
- members: HashSet::new(),
- }
- }
-}
-
-impl GroupEntry {
- pub(crate) fn add_member(&mut self, member: &str) {
- // Only ever use lowercase names, otherwise the group
- // memberships will have duplicates.
- self.members.insert(member.to_lowercase());
- }
-
- pub(crate) fn remove_member(&mut self, member: &str) {
- // Only ever use lowercase names, otherwise the group
- // memberships will have duplicates.
- self.members.remove(&member.to_lowercase());
- }
-
- pub(crate) fn into_with_member(obj: DirectoryObject, member: &str) -> Self {
- let mut g: GroupEntry = obj.into();
- g.add_member(member);
- g
- }
-
- pub(crate) fn members(&self) -> Vec<String> {
- self.members.clone().into_iter().collect::<Vec<String>>()
- }
-}
-
-impl GroupEntry {
- pub fn new(uuid: &str) -> Self {
- GroupEntry {
- uuid: uuid.to_string(),
- members: HashSet::new(),
- }
- }
-}
-
-pub(crate) struct GroupCache {
- cache: BasicCache,
-}
-
-impl GroupCache {
- pub(crate) fn new(cache_path: &str) -> Result<Self, Box<NTSTATUS>> {
- Ok(GroupCache {
- cache: BasicCache::new(cache_path)?,
- })
- }
-
- pub(crate) fn fetch(&mut self, uuid: &str) -> Option<GroupEntry> {
- self.cache.fetch::<GroupEntry>(uuid)
- }
-
- pub(crate) fn fetch_all(
- &mut self,
- ) -> Result<Vec<GroupEntry>, Box<NTSTATUS>> {
- let keys = self.cache.keys()?;
- let mut res = Vec::new();
- for uuid in keys {
- let entry = match self.cache.fetch::<GroupEntry>(&uuid) {
- Some(entry) => entry,
- None => {
- DBG_ERR!("Unable to fetch group {}", uuid);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- };
- res.push(entry);
- }
- Ok(res)
- }
-
- pub(crate) fn merge_groups(
- &mut self,
- member: &str,
- mut entries: Vec<GroupEntry>,
- ) -> Result<(), Box<NTSTATUS>> {
- // We need to ensure the member is removed from non-intersecting
- // groups, otherwise we don't honor group membership removals.
- let group_uuids: HashSet<String> = entries
- .clone()
- .into_iter()
- .map(|g| g.uuid.clone())
- .collect();
- let existing_group_uuids = {
- let cache = &self.cache;
- match cache.keys() {
- Ok(keys) => keys,
- Err(e) => {
- DBG_ERR!("Unable to fetch groups: {:?}", e);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- }
- };
- let existing_group_uuids: HashSet<String> =
- existing_group_uuids.into_iter().collect();
- let difference: HashSet<String> = existing_group_uuids
- .difference(&group_uuids)
- .cloned()
- .collect();
- for group_uuid in &difference {
- if let Some(mut group) =
- self.cache.fetch::<GroupEntry>(&group_uuid).clone()
- {
- group.remove_member(member);
- if let Err(e) =
- self.cache.store::<GroupEntry>(&group.uuid.clone(), group)
- {
- DBG_ERR!("Unable to store membership change: {:?}", e);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- }
- }
-
- // Ensure the member is added to the listed groups
- for group in &mut entries {
- group.add_member(member);
- }
-
- // Now add the new entries, merging with existing memberships
- for group in entries {
- match self.cache.fetch::<GroupEntry>(&group.uuid) {
- Some(mut existing_group) => {
- // Merge with an existing entry
- existing_group.add_member(member);
- if let Err(e) = self.cache.store::<GroupEntry>(
- &existing_group.uuid.clone(),
- existing_group,
- ) {
- DBG_ERR!("Unable to store membership change: {:?}", e);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- }
- None => {
- if let Err(e) = self
- .cache
- .store::<GroupEntry>(&group.uuid.clone(), group)
- {
- DBG_ERR!("Unable to store membership change: {:?}", e);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- }
- }
- }
- Ok(())
- }
-}
-
-pub(crate) struct PrivateCache {
- cache: BasicCache,
-}
-
-impl PrivateCache {
- pub(crate) fn new(cache_path: &str) -> Result<Self, Box<NTSTATUS>> {
- Ok(PrivateCache {
- cache: BasicCache::new(cache_path)?,
- })
- }
-
- pub(crate) fn loadable_machine_key_fetch_or_create(
- &mut self,
- hsm: &mut BoxedDynTpm,
- auth_value: &AuthValue,
- ) -> Result<LoadableMachineKey, Box<NTSTATUS>> {
- match self
- .cache
- .fetch::<LoadableMachineKey>("loadable_machine_key")
- {
- Some(loadable_machine_key) => Ok(loadable_machine_key),
- None => {
- // No machine key found - create one, and store it.
- let loadable_machine_key =
- match hsm.machine_key_create(&auth_value) {
- Ok(loadable_machine_key) => loadable_machine_key,
- Err(e) => {
- DBG_ERR!(
- "Unable to create hsm loadable \
- machine key: {:?}",
- e
- );
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- };
-
- self.cache.store::<LoadableMachineKey>(
- "loadable_machine_key",
- loadable_machine_key.clone(),
- )?;
-
- Ok(loadable_machine_key)
- }
- }
- }
-
- pub(crate) fn loadable_transport_key_fetch(
- &mut self,
- realm: &str,
- ) -> Option<LoadableMsOapxbcRsaKey> {
- let transport_key_tag = format!("{}/transport", realm);
- self.cache
- .fetch::<LoadableMsOapxbcRsaKey>(&transport_key_tag)
- }
-
- pub(crate) fn loadable_cert_key_fetch(
- &mut self,
- realm: &str,
- ) -> Option<LoadableIdentityKey> {
- let cert_key_tag = format!("{}/certificate", realm);
- self.cache.fetch::<LoadableIdentityKey>(&cert_key_tag)
- }
-
- pub(crate) fn loadable_hello_key_fetch(
- &mut self,
- account_id: &str,
- ) -> Option<LoadableIdentityKey> {
- let hello_key_tag = format!("{}/hello", account_id);
- self.cache.fetch::<LoadableIdentityKey>(&hello_key_tag)
- }
-
- pub(crate) fn loadable_cert_key_store(
- &mut self,
- realm: &str,
- cert_key: LoadableIdentityKey,
- ) -> Result<(), Box<NTSTATUS>> {
- let cert_key_tag = format!("{}/certificate", realm);
- self.cache
- .store::<LoadableIdentityKey>(&cert_key_tag, cert_key)
- }
-
- pub(crate) fn loadable_hello_key_store(
- &mut self,
- account_id: &str,
- hello_key: LoadableIdentityKey,
- ) -> Result<(), Box<NTSTATUS>> {
- let hello_key_tag = format!("{}/hello", account_id);
- self.cache
- .store::<LoadableIdentityKey>(&hello_key_tag, hello_key)
- }
-
- pub(crate) fn loadable_transport_key_store(
- &mut self,
- realm: &str,
- transport_key: LoadableMsOapxbcRsaKey,
- ) -> Result<(), Box<NTSTATUS>> {
- let transport_key_tag = format!("{}/transport", realm);
- self.cache
- .store::<LoadableMsOapxbcRsaKey>(&transport_key_tag, transport_key)
- }
-
- pub(crate) fn device_id(&mut self, realm: &str) -> Option<String> {
- let device_id_tag = format!("{}/device_id", realm);
- self.cache.fetch_str(&device_id_tag)
- }
-
- pub(crate) fn device_id_store(
- &mut self,
- realm: &str,
- device_id: &str,
- ) -> Result<(), Box<NTSTATUS>> {
- let device_id_tag = format!("{}/device_id", realm);
- self.cache.store_bytes(&device_id_tag, device_id.as_bytes())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use kanidm_hsm_crypto::soft::SoftTpm;
- use std::str::FromStr;
- use tempfile::tempdir;
-
- #[test]
- fn test_basic_cache_new() {
- let dir = tempdir().unwrap();
- let cache_path = dir.path().join("test.tdb");
- let cache = BasicCache::new(cache_path.to_str().unwrap());
- assert!(cache.is_ok());
- }
-
- #[test]
- fn test_basic_cache_store_fetch_str() {
- let dir = tempdir().unwrap();
- let cache_path = dir.path().join("test.tdb");
- let mut cache = BasicCache::new(cache_path.to_str().unwrap()).unwrap();
-
- let key = "test_key";
- let value = "test_value";
- cache.store_bytes(key, value.as_bytes()).unwrap();
- let fetched_value = cache.fetch_str(key).unwrap();
- assert_eq!(fetched_value, value);
- }
-
- #[test]
- fn test_basic_cache_store_fetch() {
- let dir = tempdir().unwrap();
- let cache_path = dir.path().join("test.tdb");
- let mut cache = BasicCache::new(cache_path.to_str().unwrap()).unwrap();
-
- let key = "test_key";
- let value = UserEntry {
- upn: "user@test.com".to_string(),
- uuid: "f63a43c7-b783-4da9-acb4-89f8ebfc49e9".to_string(),
- name: "Test User".to_string(),
- };
-
- cache.store(key, &value).unwrap();
- let fetched_value: Option<UserEntry> = cache.fetch(key);
- assert!(fetched_value.is_some());
- let fetched_value = fetched_value.unwrap();
- assert_eq!(fetched_value.upn, value.upn);
- assert_eq!(fetched_value.uuid, value.uuid);
- assert_eq!(fetched_value.name, value.name);
- }
-
- #[test]
- fn test_user_cache_store_fetch() {
- let dir = tempdir().unwrap();
- let cache_path = dir.path().join("test.tdb");
- let mut cache = UserCache::new(cache_path.to_str().unwrap()).unwrap();
-
- let entry = UserEntry {
- upn: "user@test.com".to_string(),
- uuid: "f63a43c7-b783-4da9-acb4-89f8ebfc49e9".to_string(),
- name: "Test User".to_string(),
- };
-
- cache.store(entry.clone()).unwrap();
- let fetched_entry = cache.fetch(&entry.upn);
- assert!(fetched_entry.is_some());
- let fetched_entry = fetched_entry.unwrap();
- assert_eq!(fetched_entry.upn, entry.upn);
- assert_eq!(fetched_entry.uuid, entry.uuid);
- assert_eq!(fetched_entry.name, entry.name);
- }
-
- #[test]
- fn test_uid_cache_store_fetch() {
- let dir = tempdir().unwrap();
- let cache_path = dir.path().join("test.tdb");
- let mut cache = UidCache::new(cache_path.to_str().unwrap()).unwrap();
-
- let uid: uid_t = 1000;
- let upn = "user@test.com";
-
- cache.store(uid, upn).unwrap();
- let fetched_upn = cache.fetch(uid);
- assert!(fetched_upn.is_some());
- assert_eq!(fetched_upn.unwrap(), upn);
- }
-
- #[test]
- fn test_group_cache_store_fetch() {
- let dir = tempdir().unwrap();
- let cache_path = dir.path().join("test.tdb");
- let mut cache = GroupCache::new(cache_path.to_str().unwrap()).unwrap();
-
- let mut group = GroupEntry {
- uuid: "5f8be63a-a379-4324-9f42-9ea40bed9d7f".to_string(),
- members: HashSet::new(),
- };
- group.add_member("user@test.com");
-
- cache.cache.store(&group.uuid, &group).unwrap();
- let fetched_group = cache.fetch(&group.uuid);
- assert!(fetched_group.is_some());
- let fetched_group = fetched_group.unwrap();
- assert_eq!(fetched_group.uuid, group.uuid);
- assert!(fetched_group.members.contains("user@test.com"));
- }
-
- #[test]
- fn test_private_cache_loadable_machine_key_fetch_or_create() {
- let dir = tempdir().unwrap();
- let cache_path = dir.path().join("test.tdb");
- let mut cache =
- PrivateCache::new(cache_path.to_str().unwrap()).unwrap();
-
- let mut hsm = BoxedDynTpm::new(SoftTpm::new());
- let auth_str = AuthValue::generate().expect("Failed to create hex pin");
- let auth_value = AuthValue::from_str(&auth_str)
- .expect("Unable to create auth value");
-
- let result =
- cache.loadable_machine_key_fetch_or_create(&mut hsm, &auth_value);
- assert!(result.is_ok());
-
- let fetched_key = cache
- .cache
- .fetch::<LoadableMachineKey>("loadable_machine_key");
- assert!(fetched_key.is_some());
- }
-}
+++ /dev/null
-pub const DEFAULT_ODC_PROVIDER: &str = "odc.officeapps.live.com";
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use crate::cache::{GroupCache, PrivateCache, UidCache, UserCache};
-#[cfg(not(test))]
-use crate::himmelblaud::himmelblaud_pam_auth::AuthSession;
-use bytes::{BufMut, BytesMut};
-use dbg::{DBG_DEBUG, DBG_ERR, DBG_WARNING};
-use futures::{SinkExt, StreamExt};
-use himmelblau::graph::Graph;
-use himmelblau::BrokerClientApplication;
-use idmap::Idmap;
-use kanidm_hsm_crypto::{BoxedDynTpm, MachineKey};
-use param::LoadParm;
-use sock::{PamAuthResponse, Request, Response};
-use std::error::Error;
-use std::io;
-use std::io::{Error as IoError, ErrorKind};
-use std::sync::Arc;
-use tokio::net::UnixStream;
-use tokio::sync::Mutex;
-use tokio_util::codec::{Decoder, Encoder, Framed};
-
-#[cfg(not(test))]
-pub(crate) struct Resolver {
- realm: String,
- tenant_id: String,
- lp: LoadParm,
- idmap: Idmap,
- graph: Graph,
- pcache: PrivateCache,
- user_cache: UserCache,
- uid_cache: UidCache,
- group_cache: GroupCache,
- hsm: Mutex<BoxedDynTpm>,
- machine_key: MachineKey,
- client: Arc<Mutex<BrokerClientApplication>>,
-}
-
-#[cfg(not(test))]
-impl Resolver {
- pub(crate) fn new(
- realm: &str,
- tenant_id: &str,
- lp: LoadParm,
- idmap: Idmap,
- graph: Graph,
- pcache: PrivateCache,
- user_cache: UserCache,
- uid_cache: UidCache,
- group_cache: GroupCache,
- hsm: BoxedDynTpm,
- machine_key: MachineKey,
- client: BrokerClientApplication,
- ) -> Self {
- Resolver {
- realm: realm.to_string(),
- tenant_id: tenant_id.to_string(),
- lp,
- idmap,
- graph,
- pcache,
- user_cache,
- uid_cache,
- group_cache,
- hsm: Mutex::new(hsm),
- machine_key,
- client: Arc::new(Mutex::new(client)),
- }
- }
-}
-
-// The test environment is unable to communicate with Entra ID, therefore
-// we alter the resolver to only test the cache interactions.
-
-#[cfg(test)]
-pub(crate) struct Resolver {
- realm: String,
- tenant_id: String,
- lp: LoadParm,
- idmap: Idmap,
- pcache: PrivateCache,
- user_cache: UserCache,
- uid_cache: UidCache,
- group_cache: GroupCache,
-}
-
-#[cfg(test)]
-impl Resolver {
- pub(crate) fn new(
- realm: &str,
- tenant_id: &str,
- lp: LoadParm,
- idmap: Idmap,
- pcache: PrivateCache,
- user_cache: UserCache,
- uid_cache: UidCache,
- group_cache: GroupCache,
- ) -> Self {
- Resolver {
- realm: realm.to_string(),
- tenant_id: tenant_id.to_string(),
- lp,
- idmap,
- pcache,
- user_cache,
- uid_cache,
- group_cache,
- }
- }
-}
-
-struct ClientCodec;
-
-impl Decoder for ClientCodec {
- type Error = io::Error;
- type Item = Request;
-
- fn decode(
- &mut self,
- src: &mut BytesMut,
- ) -> Result<Option<Self::Item>, Self::Error> {
- match serde_json::from_slice::<Request>(src) {
- Ok(msg) => {
- src.clear();
- Ok(Some(msg))
- }
- _ => Ok(None),
- }
- }
-}
-
-impl Encoder<Response> for ClientCodec {
- type Error = io::Error;
-
- fn encode(
- &mut self,
- msg: Response,
- dst: &mut BytesMut,
- ) -> Result<(), Self::Error> {
- DBG_DEBUG!("Attempting to send response -> {:?} ...", msg);
- let data = serde_json::to_vec(&msg).map_err(|e| {
- DBG_ERR!("socket encoding error -> {:?}", e);
- io::Error::new(ErrorKind::Other, "JSON encode error")
- })?;
- dst.put(data.as_slice());
- Ok(())
- }
-}
-
-impl ClientCodec {
- fn new() -> Self {
- ClientCodec
- }
-}
-
-pub(crate) async fn handle_client(
- stream: UnixStream,
- resolver: Arc<Mutex<Resolver>>,
-) -> Result<(), Box<dyn Error>> {
- DBG_DEBUG!("Accepted connection");
-
- let Ok(_ucred) = stream.peer_cred() else {
- return Err(Box::new(IoError::new(
- ErrorKind::Other,
- "Unable to verify peer credentials.",
- )));
- };
-
- let mut reqs = Framed::new(stream, ClientCodec::new());
- #[cfg(not(test))]
- let mut pam_auth_session_state = None;
-
- while let Some(Ok(req)) = reqs.next().await {
- let mut resolver = resolver.lock().await;
- let resp = match req {
- #[cfg(not(test))]
- Request::PamAuthenticateInit(account_id) => {
- DBG_DEBUG!("pam authenticate init");
-
- match &pam_auth_session_state {
- Some(_) => {
- DBG_WARNING!(
- "Attempt to init \
- auth session while current \
- session is active"
- );
- pam_auth_session_state = None;
- Response::Error
- }
- None => {
- let (auth_session, resp) =
- resolver.pam_auth_init(&account_id)?;
- pam_auth_session_state = Some(auth_session);
- resp
- }
- }
- }
- #[cfg(not(test))]
- Request::PamAuthenticateStep(pam_next_req) => {
- DBG_DEBUG!("pam authenticate step");
- match &mut pam_auth_session_state {
- Some(AuthSession::InProgress {
- account_id,
- cred_handler,
- }) => {
- let resp = resolver
- .pam_auth_step(
- account_id,
- cred_handler,
- pam_next_req,
- )
- .await?;
- match resp {
- Response::PamAuthStepResponse(
- PamAuthResponse::Success,
- ) => {
- pam_auth_session_state =
- Some(AuthSession::Success);
- }
- Response::PamAuthStepResponse(
- PamAuthResponse::Denied,
- ) => {
- pam_auth_session_state =
- Some(AuthSession::Denied);
- }
- _ => {}
- }
- resp
- }
- _ => {
- DBG_WARNING!(
- "Attempt to \
- continue auth session \
- while current session is \
- inactive"
- );
- Response::Error
- }
- }
- }
- Request::NssAccounts => resolver.getpwent().await?,
- Request::NssAccountByName(account_id) => {
- resolver.getpwnam(&account_id).await?
- }
- Request::NssAccountByUid(uid) => resolver.getpwuid(uid).await?,
- Request::NssGroups => resolver.getgrent().await?,
- Request::NssGroupByName(grp_id) => {
- resolver.getgrnam(&grp_id).await?
- }
- Request::NssGroupByGid(gid) => resolver.getgrgid(gid).await?,
- #[cfg(not(test))]
- Request::PamAccountAllowed(account_id) => {
- resolver.pam_acct_mgmt(&account_id).await?
- }
- Request::PamAccountBeginSession(_account_id) => Response::Success,
- #[cfg(test)]
- _ => Response::Error,
- };
- reqs.send(resp).await?;
- reqs.flush().await?;
- DBG_DEBUG!("flushed response!");
- }
-
- DBG_DEBUG!("Disconnecting client ...");
- Ok(())
-}
-
-mod himmelblaud_getgrent;
-mod himmelblaud_getgrgid;
-mod himmelblaud_getgrnam;
-mod himmelblaud_getpwent;
-mod himmelblaud_getpwnam;
-mod himmelblaud_getpwuid;
-#[cfg(not(test))]
-mod himmelblaud_pam_acct_mgmt;
-#[cfg(not(test))]
-mod himmelblaud_pam_auth;
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon implementation for nss getgrent
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use crate::himmelblaud::Resolver;
-use dbg::DBG_ERR;
-use ntstatus_gen::*;
-use sock::{Group, Response};
-
-impl Resolver {
- pub(crate) async fn getgrent(&mut self) -> Result<Response, Box<NTSTATUS>> {
- let group_entries = self.group_cache.fetch_all()?;
- let mut res = Vec::new();
- for entry in group_entries {
- let name = entry.uuid.clone();
- let gid = self
- .idmap
- .gen_to_unix(&self.tenant_id, &entry.uuid)
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_NO_SUCH_GROUP)
- })?;
- let group = Group {
- name,
- passwd: "x".to_string(),
- gid,
- members: entry.members(),
- };
- res.push(group);
- }
- Ok(Response::NssGroups(res))
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::cache::GroupEntry;
- use crate::{GroupCache, PrivateCache, UidCache, UserCache};
- use idmap::Idmap;
- use param::LoadParm;
- use std::collections::HashSet;
- use tempfile::tempdir;
-
- #[tokio::test]
- async fn test_getgrent() {
- // Create a temporary directory for the cache
- let dir = tempdir().unwrap();
-
- // Initialize the caches
- let private_cache_path = dir
- .path()
- .join("himmelblau.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let pcache = PrivateCache::new(&private_cache_path).unwrap();
- let user_cache_path = dir
- .path()
- .join("himmelblau_users.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let user_cache = UserCache::new(&user_cache_path).unwrap();
- let uid_cache_path = dir
- .path()
- .join("uid_cache.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let uid_cache = UidCache::new(&uid_cache_path).unwrap();
- let group_cache_path = dir
- .path()
- .join("himmelblau_groups.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let mut group_cache = GroupCache::new(&group_cache_path).unwrap();
-
- // Insert dummy GroupEntries into the cache
- let group_uuid1 = "c490c3ea-fd98-4d45-b6aa-2a3520f804fa";
- let group_uuid2 = "f7a51b58-84de-42a3-b5b1-967b17c04f89";
- let dummy_group1 = GroupEntry::new(group_uuid1);
- let dummy_group2 = GroupEntry::new(group_uuid2);
- group_cache
- .merge_groups("user1@test.com", vec![dummy_group1.clone()])
- .unwrap();
- group_cache
- .merge_groups("user2@test.com", vec![dummy_group2.clone()])
- .unwrap();
-
- // Initialize the Idmap with dummy configuration
- let realm = "test.com";
- let tenant_id = "89a61bb7-d1b9-4356-a1e0-75d88e06f14e";
- let mut idmap = Idmap::new().unwrap();
- idmap
- .add_gen_domain(realm, tenant_id, (1000, 2000))
- .unwrap();
-
- // Initialize dummy configuration
- let lp = LoadParm::new(None).expect("Failed loading default config");
-
- // Initialize the Resolver
- let mut resolver = Resolver {
- realm: realm.to_string(),
- tenant_id: tenant_id.to_string(),
- lp,
- idmap,
- pcache,
- user_cache,
- uid_cache,
- group_cache,
- };
-
- // Test the getgrent function
- let result = resolver.getgrent().await.unwrap();
-
- match result {
- Response::NssGroups(mut groups) => {
- groups.sort_by(|a, b| a.name.cmp(&b.name));
- assert_eq!(groups.len(), 2);
-
- let group1 = &groups[0];
- assert_eq!(group1.name, dummy_group1.uuid);
- assert_eq!(group1.gid, 1388);
- assert_eq!(group1.members, vec!["user1@test.com".to_string()]);
-
- let group2 = &groups[1];
- assert_eq!(group2.name, dummy_group2.uuid);
- assert_eq!(group2.gid, 1593);
- assert_eq!(group2.members, vec!["user2@test.com".to_string()]);
- }
- other => {
- panic!("Expected NssGroups with a list of groups: {:?}", other)
- }
- }
- }
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon implementation for nss getgrgid
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use crate::himmelblaud::Resolver;
-use libc::gid_t;
-use ntstatus_gen::NTSTATUS;
-use sock::{Group, Response};
-
-impl Resolver {
- pub(crate) async fn getgrgid(
- &mut self,
- gid: gid_t,
- ) -> Result<Response, Box<NTSTATUS>> {
- if let Some(uuid) = self.uid_cache.fetch(gid) {
- if let Some(entry) = self.group_cache.fetch(&uuid) {
- return Ok(Response::NssGroup(Some(Group {
- name: entry.uuid.clone(),
- passwd: "x".to_string(),
- gid,
- members: entry.members(),
- })));
- }
- }
- Ok(Response::NssGroup(None))
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::cache::GroupEntry;
- use crate::{GroupCache, PrivateCache, UidCache, UserCache};
- use idmap::Idmap;
- use param::LoadParm;
- use std::collections::HashSet;
- use tempfile::tempdir;
-
- #[tokio::test]
- async fn test_getgrgid() {
- // Create a temporary directory for the cache
- let dir = tempdir().unwrap();
-
- // Initialize the caches
- let private_cache_path = dir
- .path()
- .join("himmelblau.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let pcache = PrivateCache::new(&private_cache_path).unwrap();
- let user_cache_path = dir
- .path()
- .join("himmelblau_users.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let user_cache = UserCache::new(&user_cache_path).unwrap();
- let uid_cache_path = dir
- .path()
- .join("uid_cache.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let mut uid_cache = UidCache::new(&uid_cache_path).unwrap();
- let group_cache_path = dir
- .path()
- .join("himmelblau_groups.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let mut group_cache = GroupCache::new(&group_cache_path).unwrap();
-
- // Initialize the Idmap with dummy configuration
- let realm = "test.com";
- let tenant_id = "89a61bb7-d1b9-4356-a1e0-75d88e06f14e";
- let mut idmap = Idmap::new().unwrap();
- idmap
- .add_gen_domain(realm, tenant_id, (1000, 2000))
- .unwrap();
-
- // Insert a dummy GroupEntry into the cache
- let group_uuid = "c490c3ea-fd98-4d45-b6aa-2a3520f804fa".to_string();
- let dummy_gid = idmap
- .gen_to_unix(tenant_id, &group_uuid)
- .expect("Failed to map group gid");
- // Store the calculated gid -> uuid map in the cache
- uid_cache
- .store(dummy_gid, &group_uuid)
- .expect("Failed to store group gid");
- let dummy_group = GroupEntry::new(&group_uuid);
- group_cache
- .merge_groups("user1@test.com", vec![dummy_group.clone()])
- .unwrap();
-
- // Initialize dummy configuration
- let lp = LoadParm::new(None).expect("Failed loading default config");
-
- // Initialize the Resolver
- let mut resolver = Resolver {
- realm: realm.to_string(),
- tenant_id: tenant_id.to_string(),
- lp,
- idmap,
- pcache,
- user_cache,
- uid_cache,
- group_cache,
- };
-
- // Test the getgrgid function with a gid that exists
- let result = resolver.getgrgid(dummy_gid).await.unwrap();
-
- match result {
- Response::NssGroup(Some(group)) => {
- assert_eq!(group.name, dummy_group.uuid);
- assert_eq!(group.gid, dummy_gid);
- assert_eq!(group.members, vec!["user1@test.com".to_string()]);
- }
- other => panic!("Expected NssGroup with Some(group): {:?}", other),
- }
-
- // Test the getgrgid function with a gid that does not exist
- let nonexistent_gid: gid_t = 1600;
- let result = resolver.getgrgid(nonexistent_gid).await.unwrap();
- match result {
- Response::NssGroup(None) => {} // This is the expected result
- _ => panic!("Expected NssGroup with None"),
- }
- }
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon implementation for nss getgrnam
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use crate::himmelblaud::Resolver;
-use dbg::DBG_ERR;
-use ntstatus_gen::*;
-use sock::{Group, Response};
-
-impl Resolver {
- pub(crate) async fn getgrnam(
- &mut self,
- grp_id: &str,
- ) -> Result<Response, Box<NTSTATUS>> {
- let entry = match self.group_cache.fetch(grp_id) {
- Some(entry) => entry,
- None => return Ok(Response::NssGroup(None)),
- };
- let gid = self
- .idmap
- .gen_to_unix(&self.tenant_id, &entry.uuid)
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_INVALID_TOKEN)
- })?;
- // Store the calculated gid -> uuid map in the cache
- self.uid_cache.store(gid, &entry.uuid)?;
- let group = Group {
- name: entry.uuid.clone(),
- passwd: "x".to_string(),
- gid,
- members: entry.members(),
- };
- return Ok(Response::NssGroup(Some(group)));
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::cache::GroupEntry;
- use crate::{GroupCache, PrivateCache, UidCache, UserCache};
- use idmap::Idmap;
- use param::LoadParm;
- use std::collections::HashSet;
- use tempfile::tempdir;
-
- #[tokio::test]
- async fn test_getgrnam() {
- // Create a temporary directory for the cache
- let dir = tempdir().unwrap();
-
- // Initialize the caches
- let private_cache_path = dir
- .path()
- .join("himmelblau.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let pcache = PrivateCache::new(&private_cache_path).unwrap();
- let user_cache_path = dir
- .path()
- .join("himmelblau_users.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let user_cache = UserCache::new(&user_cache_path).unwrap();
- let uid_cache_path = dir
- .path()
- .join("uid_cache.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let uid_cache = UidCache::new(&uid_cache_path).unwrap();
- let group_cache_path = dir
- .path()
- .join("himmelblau_groups.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let mut group_cache = GroupCache::new(&group_cache_path).unwrap();
-
- // Insert a dummy GroupEntry into the cache
- let group_uuid = "c490c3ea-fd98-4d45-b6aa-2a3520f804fa";
- let dummy_group = GroupEntry::new(group_uuid);
- group_cache
- .merge_groups("user1@test.com", vec![dummy_group.clone()])
- .unwrap();
- group_cache
- .merge_groups("user2@test.com", vec![dummy_group.clone()])
- .unwrap();
-
- // Initialize the Idmap with dummy configuration
- let realm = "test.com";
- let tenant_id = "89a61bb7-d1b9-4356-a1e0-75d88e06f14e";
- let mut idmap = Idmap::new().unwrap();
- idmap
- .add_gen_domain(realm, tenant_id, (1000, 2000))
- .unwrap();
-
- // Initialize dummy configuration
- let lp = LoadParm::new(None).expect("Failed loading default config");
-
- // Initialize the Resolver
- let mut resolver = Resolver {
- realm: realm.to_string(),
- tenant_id: tenant_id.to_string(),
- lp,
- idmap,
- pcache,
- user_cache,
- uid_cache,
- group_cache,
- };
-
- // Test the getgrnam function with a group that exists
- let result = resolver.getgrnam(group_uuid).await.unwrap();
-
- match result {
- Response::NssGroup(Some(mut group)) => {
- group.members.sort();
- assert_eq!(group.name, dummy_group.uuid);
- assert_eq!(group.gid, 1388);
- assert_eq!(
- group.members,
- vec![
- "user1@test.com".to_string(),
- "user2@test.com".to_string()
- ]
- );
- }
- other => panic!("Expected NssGroup with Some(group): {:?}", other),
- }
-
- // Test the getgrnam function with a group that does not exist
- let nonexistent_group_uuid = "2ea8f1d4-1b94-4003-865b-cb247a8a1f5d";
- let result = resolver.getgrnam(nonexistent_group_uuid).await.unwrap();
- match result {
- Response::NssGroup(None) => {} // This is the expected result
- _ => panic!("Expected NssGroup with None"),
- }
- }
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon implementation for nss getpwent
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use crate::himmelblaud::Resolver;
-use crate::utils::split_username;
-use dbg::DBG_ERR;
-use ntstatus_gen::*;
-use sock::{Passwd, Response};
-
-impl Resolver {
- pub(crate) async fn getpwent(&mut self) -> Result<Response, Box<NTSTATUS>> {
- let user_entries = self.user_cache.fetch_all()?;
- let template_homedir = self
- .lp
- .template_homedir()
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_NOT_A_DIRECTORY)
- })?
- .ok_or_else(|| {
- DBG_ERR!("Failed to discover template homedir. Is it set?");
- Box::new(NT_STATUS_NOT_A_DIRECTORY)
- })?;
- let shell = self
- .lp
- .template_shell()
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_NOT_A_DIRECTORY)
- })?
- .ok_or_else(|| {
- DBG_ERR!("Failed to discover template shell. Is it set?");
- Box::new(NT_STATUS_NOT_A_DIRECTORY)
- })?;
- let mut res = Vec::new();
- for entry in user_entries {
- let uid = self
- .idmap
- .gen_to_unix(&self.tenant_id, &entry.upn)
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_INVALID_TOKEN)
- })?;
- let upn = entry.upn.clone();
- let (cn, domain) = match split_username(&upn) {
- Ok(res) => res,
- Err(e) => {
- DBG_ERR!(
- "Failed to parse user upn '{}': {:?}",
- &entry.upn,
- e
- );
- return Err(Box::new(
- NT_STATUS_INVALID_USER_PRINCIPAL_NAME,
- ));
- }
- };
- let homedir = template_homedir
- .clone()
- .replace("%D", &domain)
- .replace("%U", &cn);
- let passwd = Passwd {
- name: entry.upn.clone(),
- passwd: "x".to_string(),
- uid,
- gid: uid,
- gecos: entry.name,
- dir: homedir,
- shell: shell.clone(),
- };
- res.push(passwd);
- }
- Ok(Response::NssAccounts(res))
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::cache::UserEntry;
- use crate::{GroupCache, PrivateCache, UidCache, UserCache};
- use idmap::Idmap;
- use param::LoadParm;
- use std::collections::HashSet;
- use tempfile::tempdir;
-
- #[tokio::test]
- async fn test_getpwent() {
- // Create a temporary directory for the cache
- let dir = tempdir().unwrap();
-
- // Initialize the caches
- let private_cache_path = dir
- .path()
- .join("himmelblau.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let pcache = PrivateCache::new(&private_cache_path).unwrap();
- let user_cache_path = dir
- .path()
- .join("himmelblau_users.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let mut user_cache = UserCache::new(&user_cache_path).unwrap();
- let uid_cache_path = dir
- .path()
- .join("uid_cache.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let uid_cache = UidCache::new(&uid_cache_path).unwrap();
- let group_cache_path = dir
- .path()
- .join("himmelblau_groups.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let group_cache = GroupCache::new(&group_cache_path).unwrap();
-
- // Insert dummy UserEntrys into the cache
- let dummy_user = UserEntry {
- upn: "user1@test.com".to_string(),
- uuid: "731e9af3-668d-4033-afd1-9f09b9120cc7".to_string(),
- name: "User One".to_string(),
- };
- user_cache
- .store(dummy_user.clone())
- .expect("Failed storing user in cache");
-
- let dummy_user2 = UserEntry {
- upn: "user2@test.com".to_string(),
- uuid: "7be6c0c5-5763-4633-aecf-f8c460b338fd".to_string(),
- name: "User Two".to_string(),
- };
- user_cache
- .store(dummy_user2.clone())
- .expect("Failed storing user in cache");
-
- // Initialize the Idmap with dummy configuration
- let realm = "test.com";
- let tenant_id = "89a61bb7-d1b9-4356-a1e0-75d88e06f14e";
- let mut idmap = Idmap::new().unwrap();
- idmap
- .add_gen_domain(realm, tenant_id, (1000, 2000))
- .unwrap();
-
- // Initialize dummy configuration for LoadParm
- let lp = LoadParm::new(None).expect("Failed loading default config");
-
- // Initialize the Resolver
- let mut resolver = Resolver {
- realm: realm.to_string(),
- tenant_id: tenant_id.to_string(),
- lp,
- idmap,
- pcache,
- user_cache,
- uid_cache,
- group_cache,
- };
-
- // Test the getpwent function
- let result = resolver.getpwent().await.unwrap();
-
- match result {
- Response::NssAccounts(accounts) => {
- assert_eq!(accounts.len(), 2);
-
- let account1 = &accounts[0];
- assert_eq!(account1.name, dummy_user.upn);
- assert_eq!(account1.uid, 1316);
- assert_eq!(account1.gid, 1316);
- assert_eq!(account1.gecos, dummy_user.name);
- assert_eq!(account1.dir, "/home/test.com/user1");
- assert_eq!(account1.shell, "/bin/false");
-
- let account2 = &accounts[1];
- assert_eq!(account2.name, dummy_user2.upn);
- assert_eq!(account2.uid, 1671);
- assert_eq!(account2.gid, 1671);
- assert_eq!(account2.gecos, dummy_user2.name);
- assert_eq!(account2.dir, "/home/test.com/user2");
- assert_eq!(account2.shell, "/bin/false");
- }
- other => panic!(
- "Expected NssAccounts with a list of accounts: {:?}",
- other
- ),
- }
- }
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon implementation for nss getpwnam
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use crate::cache::GroupEntry;
-use crate::himmelblaud::Resolver;
-use crate::utils::split_username;
-use dbg::{DBG_ERR, DBG_WARNING};
-use ntstatus_gen::*;
-use sock::{Passwd, Response};
-
-impl Resolver {
- pub(crate) fn create_passwd_from_upn(
- &mut self,
- upn: &str,
- gecos: &str,
- ) -> Result<Passwd, Box<NTSTATUS>> {
- let template_homedir = self
- .lp
- .template_homedir()
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_NOT_A_DIRECTORY)
- })?
- .ok_or_else(|| {
- DBG_ERR!("Failed to discover template homedir. Is it set?");
- Box::new(NT_STATUS_NOT_A_DIRECTORY)
- })?;
- let shell = self
- .lp
- .template_shell()
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_NOT_A_DIRECTORY)
- })?
- .ok_or_else(|| {
- DBG_ERR!("Failed to discover template shell. Is it set?");
- Box::new(NT_STATUS_NOT_A_DIRECTORY)
- })?;
- let uid =
- self.idmap.gen_to_unix(&self.tenant_id, &upn).map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_INVALID_TOKEN)
- })?;
- // Store the calculated uid -> upn map in the cache
- self.uid_cache.store(uid, &upn)?;
- // Store the primary group (which is a fake group matching the user upn)
- let mut group = GroupEntry::new(upn);
- group.add_member(upn);
- self.group_cache.merge_groups(upn, vec![group])?;
- let (cn, domain) = match split_username(&upn) {
- Ok(res) => res,
- Err(e) => {
- DBG_ERR!("Failed to parse user upn '{}': {:?}", upn, e);
- return Err(Box::new(NT_STATUS_INVALID_USER_PRINCIPAL_NAME));
- }
- };
- let homedir = template_homedir
- .clone()
- .replace("%D", &domain)
- .replace("%U", &cn);
- let passwd = Passwd {
- name: upn.to_string(),
- passwd: "x".to_string(),
- uid,
- gid: uid,
- gecos: gecos.to_string(),
- dir: homedir,
- shell: shell.clone(),
- };
- return Ok(passwd);
- }
-
- pub(crate) async fn getpwnam(
- &mut self,
- account_id: &str,
- ) -> Result<Response, Box<NTSTATUS>> {
- // We first try to fetch the user from the cache, so that we
- // get the gecos. Otherwise we can just create a passwd entry
- // based on whether the upn exists in Entra ID.
- let entry = match self.user_cache.fetch(account_id) {
- Some(entry) => entry,
- #[cfg(not(test))]
- None => {
- // Check if the user exists in Entra ID
- let exists = match self
- .client
- .lock()
- .await
- .check_user_exists(&account_id)
- .await
- {
- Ok(exists) => exists,
- Err(e) => {
- DBG_WARNING!("{:?}", e);
- return Ok(Response::NssAccount(None));
- }
- };
- if exists {
- return Ok(Response::NssAccount(Some(
- self.create_passwd_from_upn(account_id, "")?,
- )));
- }
- return Ok(Response::NssAccount(None));
- }
- #[cfg(test)]
- None => return Ok(Response::NssAccount(None)),
- };
- return Ok(Response::NssAccount(Some(
- self.create_passwd_from_upn(&entry.upn, &entry.name)?,
- )));
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::cache::UserEntry;
- use crate::{GroupCache, PrivateCache, UidCache, UserCache};
- use idmap::Idmap;
- use param::LoadParm;
- use tempfile::tempdir;
-
- #[tokio::test]
- async fn test_getpwnam() {
- // Create a temporary directory for the cache
- let dir = tempdir().unwrap();
-
- // Initialize the caches
- let private_cache_path = dir
- .path()
- .join("himmelblau.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let pcache = PrivateCache::new(&private_cache_path).unwrap();
- let user_cache_path = dir
- .path()
- .join("himmelblau_users.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let mut user_cache = UserCache::new(&user_cache_path).unwrap();
- let uid_cache_path = dir
- .path()
- .join("uid_cache.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let uid_cache = UidCache::new(&uid_cache_path).unwrap();
- let group_cache_path = dir
- .path()
- .join("himmelblau_groups.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let group_cache = GroupCache::new(&group_cache_path).unwrap();
-
- // Insert a dummy UserEntry into the cache
- let dummy_user = UserEntry {
- upn: "user1@test.com".to_string(),
- uuid: "731e9af3-668d-4033-afd1-9f09b9120cc7".to_string(),
- name: "User One".to_string(),
- };
- let _ = user_cache.store(dummy_user.clone());
-
- // Initialize the Idmap with dummy configuration
- let realm = "test.com";
- let tenant_id = "89a61bb7-d1b9-4356-a1e0-75d88e06f14e";
- let mut idmap = Idmap::new().unwrap();
- idmap
- .add_gen_domain(realm, tenant_id, (1000, 2000))
- .unwrap();
-
- // Initialize dummy configuration for LoadParm
- let lp = LoadParm::new(None).expect("Failed loading default config");
-
- // Initialize the Resolver
- let mut resolver = Resolver {
- realm: realm.to_string(),
- tenant_id: tenant_id.to_string(),
- lp,
- idmap,
- pcache,
- user_cache,
- uid_cache,
- group_cache,
- };
-
- // Test the getpwnam function with a user that exists in the cache
- let result = resolver.getpwnam(&dummy_user.upn).await.unwrap();
-
- match result {
- Response::NssAccount(Some(account)) => {
- assert_eq!(account.name, dummy_user.upn);
- assert_eq!(account.uid, 1316);
- assert_eq!(account.gid, 1316);
- assert_eq!(account.gecos, dummy_user.name);
- assert_eq!(account.dir, "/home/test.com/user1");
- assert_eq!(account.shell, "/bin/false");
- }
- other => {
- panic!("Expected NssAccount with Some(account): {:?}", other)
- }
- }
-
- // Test the getpwnam function with a user that does not exist in the cache
- let nonexistent_user_upn = "nonexistent@test.com";
- let result = resolver.getpwnam(nonexistent_user_upn).await.unwrap();
-
- match result {
- Response::NssAccount(None) => {} // This is the expected result
- other => panic!("Expected NssAccount with None: {:?}", other),
- }
- }
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon implementation for nss getpwuid
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use crate::himmelblaud::Resolver;
-use libc::uid_t;
-use ntstatus_gen::NTSTATUS;
-use sock::Response;
-
-impl Resolver {
- pub(crate) async fn getpwuid(
- &mut self,
- uid: uid_t,
- ) -> Result<Response, Box<NTSTATUS>> {
- if let Some(upn) = self.uid_cache.fetch(uid) {
- if let Some(entry) = self.user_cache.fetch(&upn) {
- Ok(Response::NssAccount(Some(
- self.create_passwd_from_upn(&entry.upn, &entry.name)?,
- )))
- } else {
- Ok(Response::NssAccount(Some(
- self.create_passwd_from_upn(&upn, "")?,
- )))
- }
- } else {
- Ok(Response::NssAccount(None))
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::cache::UserEntry;
- use crate::{GroupCache, PrivateCache, UidCache, UserCache};
- use idmap::Idmap;
- use param::LoadParm;
- use tempfile::tempdir;
-
- #[tokio::test]
- async fn test_getpwuid() {
- // Create a temporary directory for the cache
- let dir = tempdir().unwrap();
-
- // Initialize the caches
- let private_cache_path = dir
- .path()
- .join("himmelblau.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let pcache = PrivateCache::new(&private_cache_path).unwrap();
- let user_cache_path = dir
- .path()
- .join("himmelblau_users.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let mut user_cache = UserCache::new(&user_cache_path).unwrap();
- let uid_cache_path = dir
- .path()
- .join("uid_cache.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let mut uid_cache = UidCache::new(&uid_cache_path).unwrap();
- let group_cache_path = dir
- .path()
- .join("himmelblau_groups.tdb")
- .to_str()
- .unwrap()
- .to_string();
- let group_cache = GroupCache::new(&group_cache_path).unwrap();
-
- // Insert a dummy UserEntry into the cache
- let dummy_user = UserEntry {
- upn: "user1@test.com".to_string(),
- uuid: "731e9af3-668d-4033-afd1-9f09b9120cc7".to_string(),
- name: "User One".to_string(),
- };
- let _ = user_cache.store(dummy_user.clone());
-
- // Initialize the Idmap with dummy configuration
- let realm = "test.com";
- let tenant_id = "89a61bb7-d1b9-4356-a1e0-75d88e06f14e";
- let mut idmap = Idmap::new().unwrap();
- idmap
- .add_gen_domain(realm, tenant_id, (1000, 2000))
- .unwrap();
-
- let uid = idmap
- .gen_to_unix(tenant_id, &dummy_user.upn)
- .expect("Failed to generate uid for user");
- // Store the calculated uid -> upn map in the cache
- uid_cache
- .store(uid, &dummy_user.upn)
- .expect("Failed storing generated uid in the cache");
-
- // Initialize dummy configuration for LoadParm
- let lp = LoadParm::new(None).expect("Failed loading default config");
-
- // Initialize the Resolver
- let mut resolver = Resolver {
- realm: realm.to_string(),
- tenant_id: tenant_id.to_string(),
- lp,
- idmap,
- pcache,
- user_cache,
- uid_cache,
- group_cache,
- };
-
- // Test the getpwuid function with a uid that exists in the cache
- let result = resolver.getpwuid(uid).await.unwrap();
-
- match result {
- Response::NssAccount(Some(account)) => {
- assert_eq!(account.name, dummy_user.upn);
- assert_eq!(account.uid, uid);
- assert_eq!(account.gid, uid);
- assert_eq!(account.gecos, dummy_user.name);
- assert_eq!(account.dir, "/home/test.com/user1");
- assert_eq!(account.shell, "/bin/false");
- }
- other => {
- panic!("Expected NssAccount with Some(account): {:?}", other)
- }
- }
-
- // Test the getpwuid function with a uid that does not exist in the cache
- let nonexistent_uid = 9999;
- let result = resolver.getpwuid(nonexistent_uid).await.unwrap();
-
- match result {
- Response::NssAccount(None) => {} // This is the expected result
- other => panic!("Expected NssAccount with None: {:?}", other),
- }
- }
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon implementation for pam_acct_mgmt
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use crate::himmelblaud::Resolver;
-use dbg::DBG_WARNING;
-use ntstatus_gen::*;
-use sock::Response;
-
-impl Resolver {
- pub(crate) async fn pam_acct_mgmt(
- &self,
- account_id: &str,
- ) -> Result<Response, Box<NTSTATUS>> {
- // Check if the user exists in Entra ID
- // TODO: If we're offline, check the cache instead
- match self
- .client
- .lock()
- .await
- .check_user_exists(&account_id)
- .await
- {
- Ok(exists) => Ok(Response::PamStatus(Some(exists))),
- Err(e) => {
- DBG_WARNING!("{:?}", e);
- Ok(Response::PamStatus(None))
- }
- }
- }
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon implementation for PAM_AUTH
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use crate::cache::{GroupEntry, UserEntry};
-use crate::himmelblaud::Resolver;
-use dbg::{DBG_DEBUG, DBG_ERR, DBG_INFO, DBG_WARNING};
-use himmelblau::error::{
- MsalError, AUTH_PENDING, DEVICE_AUTH_FAIL, REQUIRES_MFA,
-};
-use himmelblau::{
- DeviceAuthorizationResponse, EnrollAttrs, MFAAuthContinue,
- UserToken as UnixUserToken,
-};
-use ntstatus_gen::*;
-use serde::{Deserialize, Serialize};
-use sock::{PamAuthRequest, PamAuthResponse, Response};
-use std::env;
-use std::thread::sleep;
-use std::time::Duration;
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct GroupToken {
- pub name: String,
- pub spn: String,
- pub object_id: String,
- pub gidnumber: u32,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
-pub struct UserToken {
- pub name: String,
- pub spn: String,
- pub object_id: String,
- pub gidnumber: u32,
- pub displayname: String,
- pub shell: Option<String>,
- pub groups: Vec<GroupToken>,
-}
-
-pub(crate) enum AuthCredHandler {
- MFA { flow: MFAAuthContinue },
- DeviceAuthorizationGrant { flow: DeviceAuthorizationResponse },
- SetupPin { token: UnixUserToken },
- None,
-}
-
-pub(crate) enum AuthSession {
- InProgress {
- account_id: String,
- cred_handler: AuthCredHandler,
- },
- Success,
- Denied,
-}
-
-impl Resolver {
- pub(crate) fn pam_auth_init(
- &mut self,
- account_id: &str,
- ) -> Result<(AuthSession, Response), Box<NTSTATUS>> {
- let auth_session = AuthSession::InProgress {
- account_id: account_id.to_string(),
- cred_handler: AuthCredHandler::None,
- };
- let hello_key = self.pcache.loadable_hello_key_fetch(account_id);
- // Skip Hello authentication if it is disabled by config
- let hello_enabled =
- self.lp.himmelblaud_hello_enabled().map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- if !self.is_domain_joined() || hello_key.is_none() || !hello_enabled {
- // Send a password request to the client
- Ok((
- auth_session,
- Response::PamAuthStepResponse(PamAuthResponse::Password),
- ))
- } else {
- // Send a pin request to the client
- Ok((
- auth_session,
- Response::PamAuthStepResponse(PamAuthResponse::Pin),
- ))
- }
- }
-
- pub(crate) async fn pam_auth_step(
- &mut self,
- account_id: &str,
- cred_handler: &mut AuthCredHandler,
- pam_next_req: PamAuthRequest,
- ) -> Result<Response, Box<NTSTATUS>> {
- macro_rules! enroll_and_obtain_enrolled_token {
- ($token:ident) => {{
- if !self.is_domain_joined() {
- DBG_DEBUG!("Device is not enrolled. Enrolling now.");
- self.join_domain(&$token)
- .await
- .map_err(|e| {
- DBG_ERR!("Failed to join domain: {:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- }
- let mut tpm = self.hsm.lock().await;
- let mtoken2 = self
- .client
- .lock()
- .await
- .acquire_token_by_refresh_token(
- &$token.refresh_token,
- vec!["User.Read"],
- Some("https://graph.microsoft.com".to_string()),
- &mut tpm,
- &self.machine_key,
- )
- .await;
- match mtoken2 {
- Ok(token) => token,
- Err(e) => {
- DBG_ERR!("{:?}", e);
- match e {
- MsalError::AcquireTokenFailed(err_resp) => {
- if err_resp.error_codes.contains(&DEVICE_AUTH_FAIL) {
- /* A device authentication failure may happen
- * if Azure hasn't finished replicating the new
- * device object. Wait 5 seconds and try again. */
- DBG_INFO!("Azure hasn't finished replicating the device...");
- DBG_INFO!("Retrying in 5 seconds");
- sleep(Duration::from_secs(5));
- self.client
- .lock()
- .await
- .acquire_token_by_refresh_token(
- &$token.refresh_token,
- vec!["User.Read"],
- Some("https://graph.microsoft.com".to_string()),
- &mut tpm,
- &self.machine_key,
- )
- .await
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_NOT_FOUND)
- })?
- } else {
- return Err(Box::new(NT_STATUS_NOT_FOUND));
- }
- }
- _ => return Err(Box::new(NT_STATUS_NOT_FOUND)),
- }
- }
- }
- }};
- }
- macro_rules! auth_and_validate_hello_key {
- ($hello_key:ident, $cred:ident) => {{
- let token = {
- let mut tpm = self.hsm.lock().await;
- self
- .client
- .lock()
- .await
- .acquire_token_by_hello_for_business_key(
- account_id,
- &$hello_key,
- vec!["User.Read"],
- Some("https://graph.microsoft.com".to_string()),
- &mut tpm,
- &self.machine_key,
- &$cred,
- )
- .await
- .map_err(|e| {
- DBG_ERR!(
- "Failed to authenticate with hello key: {:?}",
- e
- );
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?
- };
-
- self.token_validate(account_id, &token).await
- }};
- }
- match (&mut *cred_handler, pam_next_req) {
- (
- AuthCredHandler::SetupPin { token },
- PamAuthRequest::SetupPin { pin },
- ) => {
- let hello_key = {
- let mut tpm = self.hsm.lock().await;
- match self
- .client
- .lock()
- .await
- .provision_hello_for_business_key(
- &token,
- &mut tpm,
- &self.machine_key,
- &pin,
- )
- .await
- {
- Ok(hello_key) => hello_key,
- Err(e) => {
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::SetupPin {
- msg: format!(
- "Failed to provision hello key: {:?}\n{}",
- e,
- "Create a PIN to use in place of passwords."
- ),
- }
- ));
- }
- }
- };
- self.pcache
- .loadable_hello_key_store(account_id, hello_key.clone())
- .map_err(|e| {
- DBG_ERR!("Failed to provision hello key: {:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
-
- auth_and_validate_hello_key!(hello_key, pin)
- }
- (_, PamAuthRequest::Pin { pin }) => {
- let hello_key = self
- .pcache
- .loadable_hello_key_fetch(account_id)
- .ok_or_else(|| {
- DBG_ERR!("Authentication failed. Hello key missing.");
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
-
- auth_and_validate_hello_key!(hello_key, pin)
- }
- (_, PamAuthRequest::Password { cred }) => {
- // Always attempt to force MFA when enrolling the device, otherwise
- // the device object will not have the MFA claim. If we are already
- // enrolled but creating a new Hello Pin, we follow the same process,
- // since only an enrollment token can be exchanged for a PRT (which
- // will be needed to enroll the Hello Pin).
- let mresp = self
- .client
- .lock()
- .await
- .initiate_acquire_token_by_mfa_flow_for_device_enrollment(
- account_id, &cred,
- )
- .await;
- // We need to wait to handle the response until after we've released
- // the lock on the client, otherwise we will deadlock.
- let resp = match mresp {
- Ok(resp) => resp,
- Err(e) => {
- // If SFA is disabled, we need to skip the SFA fallback.
- let sfa_enabled = self
- .lp
- .himmelblaud_sfa_fallback()
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- macro_rules! init_dag {
- ($msg:expr) => {{
- DBG_WARNING!(
- "SFA auth failed, falling back to DAG: {}",
- $msg
- );
- // We've exhausted alternatives, and must perform a DAG
- let resp = self
- .client
- .lock()
- .await
- .initiate_device_flow_for_device_enrollment()
- .await
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- let msg = match &resp.message {
- Some(msg) => msg.to_string(),
- None => format!("Using a browser on another \
- device, visit:\n{}\nAnd enter the code:\n{}",
- resp.verification_uri, resp.user_code),
- };
- let polling_interval = match resp.interval {
- Some(polling_interval) => polling_interval,
- None => 5,
- };
- *cred_handler = AuthCredHandler::DeviceAuthorizationGrant {
- flow: resp,
- };
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::MFAPoll {
- msg,
- polling_interval,
- }
- ));
- }}
- }
- let token = if sfa_enabled {
- DBG_WARNING!(
- "MFA auth failed, falling back to SFA: {:?}",
- e
- );
- // Again, we need to wait to handle the response until after
- // we've released the write lock on the client, otherwise we
- // will deadlock.
- match self
- .client
- .lock()
- .await
- .acquire_token_by_username_password_for_device_enrollment(
- account_id, &cred,
- )
- .await
- {
- Ok(token) => token,
- Err(e) => {
- DBG_ERR!("{:?}", e);
- match e {
- MsalError::AcquireTokenFailed(
- err_resp,
- ) => {
- if err_resp
- .error_codes
- .contains(&REQUIRES_MFA)
- {
- init_dag!(
- err_resp.error_description
- );
- }
- return Err(Box::new(
- NT_STATUS_LOGON_FAILURE,
- ));
- }
- _ => {
- return Err(Box::new(
- NT_STATUS_LOGON_FAILURE,
- ))
- }
- }
- }
- }
- } else {
- init_dag!(
- "SFA fallback is disabled by configuration"
- )
- };
- let token2 = enroll_and_obtain_enrolled_token!(token);
- return self.token_validate(account_id, &token2).await;
- }
- };
- match resp.mfa_method.as_str() {
- "PhoneAppOTP" | "OneWaySMS" | "ConsolidatedTelephony" => {
- let msg = resp.msg.clone();
- *cred_handler = AuthCredHandler::MFA { flow: resp };
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::MFACode { msg },
- ));
- }
- _ => {
- let msg = resp.msg.clone();
- let polling_interval =
- resp.polling_interval.ok_or_else(|| {
- DBG_ERR!("Invalid response from the server");
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- *cred_handler = AuthCredHandler::MFA { flow: resp };
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::MFAPoll {
- msg,
- // pam expects a polling_interval in
- // seconds, not milliseconds.
- polling_interval: polling_interval / 1000,
- },
- ));
- }
- }
- }
- (
- AuthCredHandler::DeviceAuthorizationGrant { flow },
- PamAuthRequest::MFAPoll { .. },
- ) => {
- let token = match self
- .client
- .lock()
- .await
- .acquire_token_by_device_flow(flow.clone())
- .await
- {
- Err(MsalError::AcquireTokenFailed(ref resp)) => {
- if resp.error_codes.contains(&AUTH_PENDING) {
- DBG_DEBUG!(
- "Polling for acquire_token_by_device_flow"
- );
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::MFAPollWait,
- ));
- } else {
- DBG_ERR!("{}", resp.error_description);
- return Err(Box::new(NT_STATUS_LOGON_FAILURE));
- }
- }
- Err(e) => {
- DBG_ERR!("{:?}", e);
- return Err(Box::new(NT_STATUS_LOGON_FAILURE));
- }
- Ok(token) => token,
- };
- let token2 = enroll_and_obtain_enrolled_token!(token);
- match self.token_validate(account_id, &token2).await {
- Ok(Response::PamAuthStepResponse(
- PamAuthResponse::Success,
- )) => {
- let mfa = token2.amr_mfa().map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_NOT_FOUND)
- })?;
- // If the DAG didn't obtain an MFA amr, and SFA fallback
- // is disabled, we need to reject the authentication
- // attempt here.
- let sfa_enabled = self
- .lp
- .himmelblaud_sfa_fallback()
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- if !mfa && !sfa_enabled {
- DBG_INFO!(
- "A DAG produced an SFA token, yet SFA \
- fallback is disabled by configuration"
- );
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::Denied,
- ));
- }
- // STOP! If the DAG doesn't hold an MFA amr, then we
- // need to bail out here and refuse Hello enrollment
- // (we can't enroll in Hello with an SFA token).
- // Also skip Hello enrollment if it is disabled by config
- let hello_enabled = self
- .lp
- .himmelblaud_hello_enabled()
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- if !mfa || !hello_enabled {
- if !mfa {
- DBG_INFO!(
- "Skipping Hello enrollment because \
- the token doesn't contain an MFA amr"
- );
- } else if !hello_enabled {
- DBG_INFO!(
- "Skipping Hello enrollment \
- because it is disabled"
- );
- }
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::Success,
- ));
- }
-
- // Setup Windows Hello
- *cred_handler = AuthCredHandler::SetupPin { token };
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::SetupPin {
- msg: format!(
- "Set up a PIN\n {}{}",
- "A Hello PIN is a fast, secure way to sign",
- "in to your device, apps, and services."
- ),
- },
- ));
- }
- Ok(auth_result) => Ok(auth_result),
- Err(e) => Err(e),
- }
- }
- (
- AuthCredHandler::MFA { ref mut flow },
- PamAuthRequest::MFACode { cred },
- ) => {
- let token = self
- .client
- .lock()
- .await
- .acquire_token_by_mfa_flow(
- account_id,
- Some(&cred),
- None,
- flow,
- )
- .await
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_NOT_FOUND)
- })?;
- let token2 = enroll_and_obtain_enrolled_token!(token);
- match self.token_validate(account_id, &token2).await {
- Ok(Response::PamAuthStepResponse(
- PamAuthResponse::Success,
- )) => {
- // Skip Hello enrollment if it is disabled by config
- let hello_enabled = self
- .lp
- .himmelblaud_hello_enabled()
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- if !hello_enabled {
- DBG_INFO!("Skipping Hello enrollment because it is disabled");
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::Success,
- ));
- }
-
- // Setup Windows Hello
- *cred_handler = AuthCredHandler::SetupPin { token };
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::SetupPin {
- msg: format!(
- "Set up a PIN\n {}{}",
- "A Hello PIN is a fast, secure way to sign",
- "in to your device, apps, and services."
- ),
- },
- ));
- }
- Ok(auth_result) => Ok(auth_result),
- Err(e) => Err(e),
- }
- }
- (
- AuthCredHandler::MFA { flow },
- PamAuthRequest::MFAPoll { poll_attempt },
- ) => {
- let max_poll_attempts =
- flow.max_poll_attempts.ok_or_else(|| {
- DBG_ERR!("Invalid response from the server");
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- if poll_attempt > max_poll_attempts {
- DBG_ERR!("MFA polling timed out");
- return Err(Box::new(NT_STATUS_LOGON_FAILURE));
- }
- let token = match self
- .client
- .lock()
- .await
- .acquire_token_by_mfa_flow(
- account_id,
- None,
- Some(poll_attempt),
- flow,
- )
- .await
- {
- Ok(token) => token,
- Err(e) => match e {
- MsalError::MFAPollContinue => {
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::MFAPollWait,
- ));
- }
- e => {
- DBG_ERR!("{:?}", e);
- return Err(Box::new(NT_STATUS_NOT_FOUND));
- }
- },
- };
- let token2 = enroll_and_obtain_enrolled_token!(token);
- match self.token_validate(account_id, &token2).await {
- Ok(Response::PamAuthStepResponse(
- PamAuthResponse::Success,
- )) => {
- // Skip Hello enrollment if it is disabled by config
- let hello_enabled = self
- .lp
- .himmelblaud_hello_enabled()
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- if !hello_enabled {
- DBG_INFO!("Skipping Hello enrollment because it is disabled");
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::Success,
- ));
- }
-
- // Setup Windows Hello
- *cred_handler = AuthCredHandler::SetupPin { token };
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::SetupPin {
- msg: format!(
- "Set up a PIN\n {}{}",
- "A Hello PIN is a fast, secure way to sign",
- "in to your device, apps, and services."
- ),
- },
- ));
- }
- Ok(auth_result) => Ok(auth_result),
- Err(e) => Err(e),
- }
- }
- _ => {
- DBG_ERR!(
- "Unexpected AuthCredHandler and PamAuthRequest pairing"
- );
- Err(Box::new(NT_STATUS_NOT_IMPLEMENTED))
- }
- }
- }
-
- async fn token_validate(
- &mut self,
- account_id: &str,
- token: &UnixUserToken,
- ) -> Result<Response, Box<NTSTATUS>> {
- match &token.access_token {
- Some(access_token) => {
- /* MFA can respond with different user than requested.
- * Azure resource names are case insensitive.
- */
- let spn = token.spn().map_err(|e| {
- DBG_ERR!("Failed fetching user spn: {:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- if account_id.to_string().to_lowercase()
- != spn.to_string().to_lowercase()
- {
- DBG_ERR!(
- "Authenticated user {} does not match requested user {}",
- spn, account_id
- );
- return Ok(Response::PamAuthStepResponse(
- PamAuthResponse::Denied,
- ));
- }
- DBG_INFO!(
- "Authentication successful for user '{}'",
- account_id
- );
-
- // Store the user in the cache
- let user_entry: UserEntry = token.try_into().map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- self.user_cache.store(user_entry).map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
-
- // Get the users groups, and store those groups in the cache
- let groups: Vec<GroupEntry> = self
- .graph
- .request_user_groups(access_token)
- .await
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?
- .into_iter()
- .map(|g| GroupEntry::into_with_member(g, account_id))
- .collect();
- self.group_cache.merge_groups(account_id, groups).map_err(
- |e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- },
- )?;
-
- Ok(Response::PamAuthStepResponse(PamAuthResponse::Success))
- }
- None => {
- DBG_INFO!("Authentication failed for user '{}'", account_id);
- Err(Box::new(NT_STATUS_NOT_FOUND))
- }
- }
- }
-
- async fn join_domain(
- &mut self,
- token: &UnixUserToken,
- ) -> Result<(), Box<NTSTATUS>> {
- /* If not already joined, join the domain now. */
- let attrs = EnrollAttrs::new(
- self.realm.clone(),
- None,
- Some(env::consts::OS.to_string()),
- None,
- None,
- )
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- let mut tpm = self.hsm.lock().await;
- match self
- .client
- .lock()
- .await
- .enroll_device(
- &token.refresh_token,
- attrs,
- &mut tpm,
- &self.machine_key,
- )
- .await
- {
- Ok((
- new_loadable_transport_key,
- new_loadable_cert_key,
- device_id,
- )) => {
- DBG_INFO!("Joined domain {} ({})", self.realm, device_id);
- // Store the new_loadable_cert_key in the keystore
- self.pcache
- .loadable_cert_key_store(&self.realm, new_loadable_cert_key)
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- // Store the new_loadable_transport_key
- self.pcache
- .loadable_transport_key_store(
- &self.realm,
- new_loadable_transport_key,
- )
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- // Store the device_id
- self.pcache
- .device_id_store(&self.realm, &device_id)
- .map_err(|e| {
- DBG_ERR!("{:?}", e);
- Box::new(NT_STATUS_LOGON_FAILURE)
- })?;
- Ok(())
- }
- Err(e) => {
- DBG_ERR!("{:?}", e);
- Err(Box::new(NT_STATUS_LOGON_FAILURE))
- }
- }
- }
-
- fn is_domain_joined(&mut self) -> bool {
- /* If we have access to tpm keys, and the domain device_id is
- * configured, we'll assume we are domain joined. */
- let device_id = self.pcache.device_id(&self.realm);
- if device_id.is_none() {
- return false;
- }
- let transport_key =
- self.pcache.loadable_transport_key_fetch(&self.realm);
- if transport_key.is_none() {
- return false;
- }
- let cert_key = self.pcache.loadable_cert_key_fetch(&self.realm);
- if cert_key.is_none() {
- return false;
- }
- true
- }
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-// Ignore unused/dead code when running cargo test
-#![cfg_attr(test, allow(unused_imports))]
-#![cfg_attr(test, allow(dead_code))]
-
-use clap::{Arg, ArgAction, Command};
-use dbg::*;
-use himmelblau::graph::Graph;
-use himmelblau::BrokerClientApplication;
-use idmap::Idmap;
-use kanidm_hsm_crypto::soft::SoftTpm;
-use kanidm_hsm_crypto::{BoxedDynTpm, Tpm};
-use libc::umask;
-use param::LoadParm;
-use std::path::{Path, PathBuf};
-use std::process::ExitCode;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::sync::Arc;
-use tokio::net::UnixListener;
-use tokio::signal::unix::{signal, SignalKind};
-use tokio::sync::Mutex;
-
-mod constants;
-use constants::DEFAULT_ODC_PROVIDER;
-mod cache;
-mod himmelblaud;
-use cache::{GroupCache, PrivateCache, UidCache, UserCache};
-mod utils;
-
-#[cfg(not(test))]
-#[tokio::main(flavor = "current_thread")]
-async fn main() -> ExitCode {
- let clap_args = Command::new("himmelblaud")
- .version(env!("CARGO_PKG_VERSION"))
- .about("Samba Himmelblau Authentication Daemon")
- .arg(
- Arg::new("debuglevel")
- .help("Set debug level")
- .short('d')
- .long("debuglevel")
- .value_parser(
- clap::value_parser!(u16).range(0..(MAX_DEBUG_LEVEL as i64)),
- )
- .action(ArgAction::Set),
- )
- .arg(
- Arg::new("debug-stdout")
- .help("Send debug output to standard output")
- .long("debug-stdout")
- .action(ArgAction::SetTrue),
- )
- .arg(
- Arg::new("configfile")
- .help("Use alternative configuration file")
- .short('s')
- .long("configfile")
- .action(ArgAction::Set),
- )
- .get_matches();
-
- let stop_now = Arc::new(AtomicBool::new(false));
- let terminate_now = Arc::clone(&stop_now);
- let quit_now = Arc::clone(&stop_now);
- let interrupt_now = Arc::clone(&stop_now);
-
- let frame = talloc::talloc_stackframe!();
-
- async {
- // Set the command line debug level
- if let Some(debuglevel) = clap_args.get_one::<u16>("debuglevel") {
- debuglevel_set!(*debuglevel);
- }
-
- // Initialize the LoadParm from the command line specified config file
- let lp = match LoadParm::new(
- clap_args
- .get_one::<String>("configfile")
- .map(|x| x.as_str()),
- ) {
- Ok(lp) => lp,
- Err(e) => {
- eprintln!("Failed loading smb.conf: {:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
-
- // Check that the realm is configured. This the bare minimum for
- // himmelblaud, since a device join can happen at authentication time,
- // but we need to know the permitted enrollment domain.
- let realm = match lp.realm() {
- Ok(Some(realm)) => realm,
- _ => {
- eprintln!(
- "The realm MUST be set in the \
- smb.conf to start himmelblaud"
- );
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
-
- // Setup logging, either to the configured logfile, or to stdout, depending
- // on what is specified on the command line.
- match clap_args.get_flag("debug-stdout") {
- true => setup_logging(env!("CARGO_PKG_NAME"), DEBUG_STDOUT),
- false => {
- setup_logging(env!("CARGO_PKG_NAME"), DEBUG_FILE);
- match lp.logfile() {
- Ok(Some(logfile)) => debug_set_logfile(&logfile),
- _ => {
- eprintln!("Failed to determine logfile name");
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- }
- }
- }
-
- // Determine the unix socket path
- let sock_dir_str = match lp.winbindd_socket_directory() {
- Ok(Some(sock_dir)) => sock_dir,
- _ => {
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
- let sock_dir = Path::new(&sock_dir_str);
- let mut sock_path_buf = PathBuf::from(sock_dir);
- sock_path_buf.push("hb_pipe");
- let sock_path = match sock_path_buf.to_str() {
- Some(sock_path) => sock_path,
- None => {
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
- if sock_path_buf.exists() {
- DBG_DEBUG!("Cleaning up socket from previous invocations");
- if let Err(_) = std::fs::remove_file(sock_path) {
- DBG_ERR!("Failed removing socket");
- }
- }
-
- // Initialize the Himmelblau cache
- let private_cache_path = match lp.private_path("himmelblau.tdb") {
- Ok(Some(private_cache_path)) => private_cache_path,
- _ => {
- DBG_ERR!("Failed to determine private cache path");
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
- let mut private_dir = Path::new(&private_cache_path).to_path_buf();
- private_dir.pop();
- if !private_dir.exists() {
- DBG_ERR!(
- "The private directory '{}' does not exist",
- private_dir.display()
- );
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- let mut pcache = match PrivateCache::new(&private_cache_path) {
- Ok(cache) => cache,
- Err(e) => {
- DBG_ERR!(
- "Failed to open the himmelblau private cache: {:?}",
- e
- );
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
-
- let cache_dir = match lp.cache_directory() {
- Ok(Some(cache_dir)) => cache_dir,
- _ => {
- DBG_ERR!("Failed to determine cache directory");
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
- if !Path::new(&cache_dir).exists() {
- DBG_ERR!("The cache directory '{}' does not exist", cache_dir);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
-
- let user_cache_path = Path::new(&cache_dir)
- .join("himmelblau_users.tdb")
- .display()
- .to_string();
- let user_cache = match UserCache::new(&user_cache_path) {
- Ok(cache) => cache,
- Err(e) => {
- DBG_ERR!("Failed to open the himmelblau user cache: {:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
-
- let uid_cache_path = Path::new(&cache_dir)
- .join("himmelblau_uid_map.tdb")
- .display()
- .to_string();
- let uid_cache = match UidCache::new(&uid_cache_path) {
- Ok(cache) => cache,
- Err(e) => {
- DBG_ERR!("Failed to open the himmelblau uid cache: {:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
-
- let group_cache_path = Path::new(&cache_dir)
- .join("himmelblau_groups.tdb")
- .display()
- .to_string();
- let group_cache = match GroupCache::new(&group_cache_path) {
- Ok(cache) => cache,
- Err(e) => {
- DBG_ERR!("Failed to open the himmelblau group cache: {:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
-
- // Check for and create the hsm pin if required.
- let hsm_pin_path = match lp.himmelblaud_hsm_pin_path() {
- Ok(Some(hsm_pin_path)) => hsm_pin_path,
- _ => {
- DBG_ERR!("Failed loading hsm pin path.");
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
- let mut hsm_pin_dir = Path::new(&hsm_pin_path).to_path_buf();
- hsm_pin_dir.pop();
- if !hsm_pin_dir.exists() {
- DBG_ERR!(
- "The hsm pin directory '{}' does not exist",
- hsm_pin_dir.display()
- );
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- let auth_value =
- match utils::hsm_pin_fetch_or_create(&hsm_pin_path).await {
- Ok(auth_value) => auth_value,
- Err(e) => {
- DBG_ERR!("{:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
-
- // Setup the HSM and its machine key
- let mut hsm: BoxedDynTpm = BoxedDynTpm::new(SoftTpm::new());
-
- let loadable_machine_key = match pcache
- .loadable_machine_key_fetch_or_create(&mut hsm, &auth_value)
- {
- Ok(lmk) => lmk,
- Err(e) => {
- DBG_ERR!("{:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
-
- let res = hsm.machine_key_load(&auth_value, &loadable_machine_key);
- let machine_key = match res {
- Ok(machine_key) => machine_key,
- Err(e) => {
- DBG_ERR!("Unable to load machine root key: {:?}", e);
- DBG_INFO!("This can occur if you have changed your HSM pin.");
- DBG_INFO!(
- "To proceed, run `tdbtool erase {}`",
- private_cache_path
- );
- DBG_INFO!("The host will forget domain enrollments.");
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
-
- // Get the transport key for a joined domain
- let loadable_transport_key =
- pcache.loadable_transport_key_fetch(&realm);
-
- // Get the certificate key for a joined domain
- let loadable_cert_key = pcache.loadable_cert_key_fetch(&realm);
-
- // Contact the odc provider to get the authority host and tenant id
- let graph = match Graph::new(DEFAULT_ODC_PROVIDER, &realm).await {
- Ok(graph) => graph,
- Err(e) => {
- DBG_ERR!("Failed initializing the graph: {:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
- let authority_host = graph.authority_host();
- let tenant_id = graph.tenant_id();
- let authority = format!("https://{}/{}", authority_host, tenant_id);
-
- let client = match BrokerClientApplication::new(
- Some(&authority),
- loadable_transport_key,
- loadable_cert_key,
- ) {
- Ok(client) => client,
- Err(e) => {
- DBG_ERR!("Failed initializing the broker: {:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
-
- let mut idmap = match Idmap::new() {
- Ok(idmap) => idmap,
- Err(e) => {
- DBG_ERR!("Failed initializing the idmapper: {:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
- // Configure the idmap range
- let (low, high) = match lp.idmap_range(&realm) {
- Ok(res) => res,
- Err(e) => {
- DBG_ERR!("Failed fetching idmap range: {:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
- if let Err(e) = idmap.add_gen_domain(&realm, &tenant_id, (low, high)) {
- DBG_ERR!("Failed adding the domain idmap range: {:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
-
- let resolver = Arc::new(Mutex::new(himmelblaud::Resolver::new(
- &realm,
- &tenant_id,
- lp,
- idmap,
- graph,
- pcache,
- user_cache,
- uid_cache,
- group_cache,
- hsm,
- machine_key,
- client,
- )));
-
- // Set the umask while we open the path for most clients.
- let before = unsafe { umask(0) };
- // Listen for incoming requests from PAM and NSS
- let listener = match UnixListener::bind(sock_path) {
- Ok(listener) => listener,
- Err(e) => {
- DBG_ERR!("Failed setting up the socket listener: {:?}", e);
- talloc::TALLOC_FREE!(frame);
- return ExitCode::FAILURE;
- }
- };
- // Undo umask changes.
- let _ = unsafe { umask(before) };
-
- let server = tokio::spawn(async move {
- while !stop_now.load(Ordering::Relaxed) {
- let resolver_ref = resolver.clone();
- match listener.accept().await {
- Ok((socket, _addr)) => {
- tokio::spawn(async move {
- if let Err(e) = himmelblaud::handle_client(
- socket,
- resolver_ref.clone(),
- )
- .await
- {
- DBG_ERR!(
- "handle_client error occurred: {:?}",
- e
- );
- }
- });
- }
- Err(e) => {
- DBG_ERR!("Error while handling connection: {:?}", e);
- }
- }
- }
- });
-
- let terminate_task = tokio::spawn(async move {
- match signal(SignalKind::terminate()) {
- Ok(mut stream) => {
- stream.recv().await;
- terminate_now.store(true, Ordering::Relaxed);
- }
- Err(e) => {
- DBG_ERR!("Failed registering terminate signal: {}", e);
- }
- };
- });
-
- let quit_task = tokio::spawn(async move {
- match signal(SignalKind::quit()) {
- Ok(mut stream) => {
- stream.recv().await;
- quit_now.store(true, Ordering::Relaxed);
- }
- Err(e) => {
- DBG_ERR!("Failed registering quit signal: {}", e);
- }
- };
- });
-
- let interrupt_task = tokio::spawn(async move {
- match signal(SignalKind::interrupt()) {
- Ok(mut stream) => {
- stream.recv().await;
- interrupt_now.store(true, Ordering::Relaxed);
- }
- Err(e) => {
- DBG_ERR!("Failed registering interrupt signal: {}", e);
- }
- };
- });
-
- DBG_INFO!("Server started ...");
-
- tokio::select! {
- _ = server => {
- DBG_DEBUG!("Main listener task is terminating");
- },
- _ = terminate_task => {
- DBG_DEBUG!("Received signal to terminate");
- },
- _ = quit_task => {
- DBG_DEBUG!("Received signal to quit");
- },
- _ = interrupt_task => {
- DBG_DEBUG!("Received signal to interrupt");
- }
- }
-
- talloc::TALLOC_FREE!(frame);
- ExitCode::SUCCESS
- }
- .await
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Himmelblau daemon common utilities
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use dbg::{DBG_ERR, DBG_INFO};
-use kanidm_hsm_crypto::AuthValue;
-use ntstatus_gen::*;
-use std::path::PathBuf;
-use std::str::FromStr;
-use tokio::fs::File;
-use tokio::io::AsyncReadExt;
-
-pub fn split_username(
- username: &str,
-) -> Result<(String, String), Box<NTSTATUS>> {
- let tup: Vec<&str> = username.split('@').collect();
- if tup.len() == 2 {
- return Ok((tup[0].to_string(), tup[1].to_string()));
- }
- Err(Box::new(NT_STATUS_INVALID_USER_PRINCIPAL_NAME))
-}
-
-pub(crate) async fn hsm_pin_fetch_or_create(
- hsm_pin_path: &str,
-) -> Result<AuthValue, Box<NTSTATUS>> {
- let auth_value = if !PathBuf::from_str(hsm_pin_path)
- .map_err(|e| {
- DBG_ERR!("Failed to create hsm pin: {:?}", e);
- Box::new(NT_STATUS_UNSUCCESSFUL)
- })?
- .exists()
- {
- let auth_value = AuthValue::generate().map_err(|e| {
- DBG_ERR!("Failed to create hsm pin: {:?}", e);
- Box::new(NT_STATUS_UNSUCCESSFUL)
- })?;
- std::fs::write(hsm_pin_path, auth_value.clone()).map_err(|e| {
- DBG_ERR!("Failed to write hsm pin: {:?}", e);
- Box::new(NT_STATUS_UNSUCCESSFUL)
- })?;
-
- DBG_INFO!("Generated new HSM pin");
- auth_value
- } else {
- let mut file = File::open(hsm_pin_path).await.map_err(|e| {
- DBG_ERR!("Failed to read hsm pin: {:?}", e);
- Box::new(NT_STATUS_UNSUCCESSFUL)
- })?;
- let mut auth_value = vec![];
- file.read_to_end(&mut auth_value).await.map_err(|e| {
- DBG_ERR!("Failed to read hsm pin: {:?}", e);
- Box::new(NT_STATUS_UNSUCCESSFUL)
- })?;
- std::str::from_utf8(&auth_value)
- .map_err(|e| {
- DBG_ERR!("Failed to read hsm pin: {:?}", e);
- Box::new(NT_STATUS_UNSUCCESSFUL)
- })?
- .to_string()
- };
- AuthValue::try_from(auth_value.as_bytes()).map_err(|e| {
- DBG_ERR!("Invalid hsm pin: {:?}", e);
- Box::new(NT_STATUS_UNSUCCESSFUL)
- })
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::io::Write;
- use tempfile::tempdir;
- use tokio::fs;
-
- #[test]
- fn test_split_username_success() {
- let username = "user@domain.com";
- let result = split_username(username);
- assert!(result.is_ok());
- let (user, domain) = result.unwrap();
- assert_eq!(user, "user");
- assert_eq!(domain, "domain.com");
- }
-
- #[test]
- fn test_split_username_failure() {
- let username = "invalid_username";
- let result = split_username(username);
- assert!(result.is_err());
- assert_eq!(*result.unwrap_err(), NT_STATUS_INVALID_USER_PRINCIPAL_NAME);
- }
-
- #[tokio::test]
- async fn test_hsm_pin_fetch_or_create_generate() {
- let dir = tempdir().unwrap();
- let path = dir.path().join("hsm_pin");
-
- let result = hsm_pin_fetch_or_create(path.to_str().unwrap()).await;
- assert!(result.is_ok());
-
- // Verify that the file is created and contains a valid auth value
- let saved_pin = fs::read(path).await.expect("Auth value missing");
- AuthValue::try_from(saved_pin.as_slice())
- .expect("Failed parsing auth value");
- }
-
- #[tokio::test]
- async fn test_hsm_pin_fetch_or_create_invalid_path() {
- let result = hsm_pin_fetch_or_create("invalid_path\0").await;
- assert!(result.is_err());
- match result {
- Err(e) => assert_eq!(*e, NT_STATUS_UNSUCCESSFUL),
- Ok(_) => panic!("Expected error but got success"),
- }
- }
-
- #[tokio::test]
- async fn test_hsm_pin_fetch_or_create_invalid_auth_value() {
- let dir = tempdir().unwrap();
- let path = dir.path().join("hsm_pin");
-
- // Write invalid content to the file
- let mut file = std::fs::File::create(&path).unwrap();
- file.write_all(b"invalid_auth_value").unwrap();
-
- // Test reading the invalid file
- let result = hsm_pin_fetch_or_create(path.to_str().unwrap()).await;
- assert!(result.is_err());
- match result {
- Err(e) => assert_eq!(*e, NT_STATUS_UNSUCCESSFUL),
- Ok(_) => panic!("Expected error but got success"),
- }
- }
-}
+++ /dev/null
-[package]
-name = "idmap"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[dependencies]
-chelps.workspace = true
-dbg.workspace = true
-libc.workspace = true
-
-[build-dependencies]
-cc = "1.0.97"
-bindgen = "0.69.4"
+++ /dev/null
-use std::env;
-use std::path::Path;
-use std::path::PathBuf;
-
-fn main() {
- cc::Build::new()
- .file("src/sss_idmap.c")
- .file("src/sss_idmap_conv.c")
- .file("src/murmurhash3.c")
- .include(Path::new("../../bin/default/include"))
- .warnings(false)
- .compile("sss_idmap");
-
- let bindings = bindgen::Builder::default()
- .blocklist_function("qgcvt")
- .blocklist_function("qgcvt_r")
- .blocklist_function("qfcvt")
- .blocklist_function("qfcvt_r")
- .blocklist_function("qecvt")
- .blocklist_function("qecvt_r")
- .blocklist_function("strtold")
- .header("src/sss_idmap.h")
- .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
- .generate()
- .expect("Unable to generate bindings");
-
- let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
- 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/");
-}
+++ /dev/null
-/*
- 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 <http://www.gnu.org/licenses/>.
-*/
-use dbg::DBG_ERR;
-use std::ffi::CString;
-use std::fmt;
-use std::ptr;
-use std::sync::{Arc, Mutex};
-
-mod ffi {
- #![allow(non_upper_case_globals)]
- #![allow(non_camel_case_types)]
- #![allow(non_snake_case)]
- #![allow(dead_code)]
- include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-}
-
-#[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)
- }
-}
-
-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 {}
-
-pub struct Idmap {
- ctx: Arc<Mutex<*mut ffi::sss_idmap_ctx>>,
-}
-
-impl Idmap {
- pub fn new() -> Result<Idmap, IdmapError> {
- let mut ctx = ptr::null_mut();
- unsafe {
- match IdmapError(ffi::sss_idmap_init(
- None,
- ptr::null_mut(),
- None,
- &mut ctx,
- )) {
- IDMAP_SUCCESS => Ok(Idmap {
- ctx: Arc::new(Mutex::new(ctx)),
- }),
- e => Err(e),
- }
- }
- }
-
- pub fn add_gen_domain(
- &mut self,
- domain_name: &str,
- tenant_id: &str,
- range: (u32, u32),
- ) -> Result<(), IdmapError> {
- let ctx = self.ctx.lock().map_err(|e| {
- DBG_ERR!("Failed obtaining write lock on sss_idmap_ctx: {}", e);
- IDMAP_ERROR
- })?;
- 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,
- };
- unsafe {
- match IdmapError(ffi::sss_idmap_add_gen_domain_ex(
- *ctx,
- domain_name_cstr.as_ptr(),
- tenant_id_cstr.as_ptr(),
- &mut idmap_range,
- ptr::null_mut(),
- None,
- None,
- ptr::null_mut(),
- 0,
- false,
- )) {
- IDMAP_SUCCESS => Ok(()),
- e => Err(e),
- }
- }
- }
-
- pub fn gen_to_unix(
- &self,
- tenant_id: &str,
- input: &str,
- ) -> Result<u32, IdmapError> {
- let ctx = self.ctx.lock().map_err(|e| {
- DBG_ERR!("Failed obtaining write lock on sss_idmap_ctx: {}", e);
- IDMAP_ERROR
- })?;
- 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 IdmapError(ffi::sss_idmap_gen_to_unix(
- *ctx,
- tenant_id_cstr.as_ptr(),
- input_cstr.as_ptr(),
- &mut id,
- )) {
- IDMAP_SUCCESS => Ok(id),
- e => Err(e),
- }
- }
- }
-}
-
-impl Drop for Idmap {
- fn drop(&mut self) {
- match self.ctx.lock() {
- Ok(ctx) => unsafe {
- let _ = ffi::sss_idmap_free(*ctx);
- },
- Err(e) => {
- DBG_ERR!(
- "Failed obtaining write lock on sss_idmap_ctx during drop: {}",
- e
- );
- }
- }
- }
-}
-
-unsafe impl Send for Idmap {}
-unsafe impl Sync for Idmap {}
-
-#[cfg(test)]
-mod tests {
- use crate::Idmap;
- use std::collections::HashMap;
- pub const DEFAULT_IDMAP_RANGE: (u32, u32) = (200000, 2000200000);
-
- #[test]
- fn sssd_idmapping() {
- let domain = "contoso.onmicrosoft.com";
- let tenant_id = "d7af6c1b-0497-40fe-9d17-07e6b0f8332e";
- let mut idmap = Idmap::new().expect("Idmap initialization failed");
-
- idmap
- .add_gen_domain(domain, tenant_id, DEFAULT_IDMAP_RANGE)
- .expect("Failed initializing test domain idmapping");
-
- // Verify we always get the same mapping for various users
- let mut usermap: HashMap<String, u32> = HashMap::new();
- usermap.insert("tux@contoso.onmicrosoft.com".to_string(), 1912749799);
- usermap.insert("admin@contoso.onmicrosoft.com".to_string(), 297515919);
- usermap.insert("dave@contoso.onmicrosoft.com".to_string(), 132631922);
- usermap.insert("joe@contoso.onmicrosoft.com".to_string(), 361591965);
- usermap.insert("georg@contoso.onmicrosoft.com".to_string(), 866887005);
-
- for (username, expected_uid) in &usermap {
- let uid = idmap.gen_to_unix(tenant_id, username).expect(&format!(
- "Failed converting username {} to uid",
- username
- ));
- assert_eq!(
- uid, *expected_uid,
- "Uid for {} did not match",
- username
- );
- }
- }
-}
+++ /dev/null
-/* 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 <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "config.h"
-#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;
-}
+++ /dev/null
-/* 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 <stdint.h>
-
-uint32_t murmurhash3(const char *key, int len, uint32_t seed);
-
-#endif /* _SHARED_MURMURHASH3_H_ */
+++ /dev/null
-/*
- SSSD
-
- ID-mapping library
-
- Authors:
- Sumit Bose <sbose@redhat.com>
-
- 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 <http://www.gnu.org/licenses/>.
-*/
-
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <utf8proc.h>
-
-#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;
-}
+++ /dev/null
-/*
- SSSD
-
- ID-mapping library
-
- Authors:
- Sumit Bose <sbose@redhat.com>
-
- 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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef SSS_IDMAP_H_
-#define SSS_IDMAP_H_
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <sys/types.h>
-
-#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_ */
+++ /dev/null
-/*
- SSSD
-
- ID-mapping library - conversion utilities
-
- Authors:
- Sumit Bose <sbose@redhat.com>
-
- 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 <http://www.gnu.org/licenses/>.
-*/
-
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <ctype.h>
-
-#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;
-}
+++ /dev/null
-/*
- SSSD
-
- ID-mapping library - private headers
-
- Authors:
- Sumit Bose <sbose@redhat.com>
-
- 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 <http://www.gnu.org/licenses/>.
-*/
-
-#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_ */
+++ /dev/null
-/*
- 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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef _UTILS_H
-#define _UTILS_H
-
-#include <string.h>
-#include <stdint.h>
-
-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 */
+++ /dev/null
-[package]
-name = "nss_himmelblau"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[lib]
-name = "nss_himmelblau"
-crate-type = [ "cdylib" ]
-path = "src/lib.rs"
-
-[dependencies]
-libc.workspace = true
-libnss = "0.8.0"
-ntstatus_gen.workspace = true
-param = { workspace = true }
-sock = { workspace = true }
-
-[build-dependencies]
-version = { path = "../version" }
+++ /dev/null
-fn main() {
- if let Some(vers) = version::samba_version_string() {
- println!("cargo:rustc-env=CARGO_PKG_VERSION={}", vers);
- }
- println!(
- "cargo:rustc-env=CARGO_PKG_VERSION_MAJOR={}",
- version::SAMBA_VERSION_MAJOR
- );
- println!(
- "cargo:rustc-env=CARGO_PKG_VERSION_MINOR={}",
- version::SAMBA_VERSION_MINOR
- );
- println!(
- "cargo:rustc-env=CARGO_PKG_VERSION_PATCH={}",
- version::SAMBA_VERSION_RELEASE
- );
- println!("cargo:rustc-env=LD_LIBRARY_PATH=../../bin/shared:../../bin/shared/private/");
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- NSS module for the Himmelblau daemon
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#[macro_use]
-extern crate libnss;
-
-use libnss::group::{Group as NssGroup, GroupHooks};
-use libnss::interop::Response as NssResponse;
-use libnss::passwd::{Passwd as NssPasswd, PasswdHooks};
-use param::LoadParm;
-use sock::{stream_and_timeout, Group, Passwd, Request, Response};
-
-struct HimmelblauPasswd;
-libnss_passwd_hooks!(himmelblau, HimmelblauPasswd);
-
-impl PasswdHooks for HimmelblauPasswd {
- fn get_all_entries() -> NssResponse<Vec<NssPasswd>> {
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return NssResponse::Unavail,
- };
- let (mut stream, timeout) = match stream_and_timeout(&lp) {
- Ok((stream, timeout)) => (stream, timeout),
- Err(_) => return NssResponse::Unavail,
- };
-
- let req = Request::NssAccounts;
- stream
- .send(&req, timeout)
- .map(|r| match r {
- Response::NssAccounts(l) => {
- l.into_iter().map(|passwd| passwd.into()).collect()
- }
- _ => vec![],
- })
- .map(NssResponse::Success)
- .unwrap_or_else(|_| NssResponse::Success(vec![]))
- }
-
- fn get_entry_by_uid(uid: libc::uid_t) -> NssResponse<NssPasswd> {
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return NssResponse::Unavail,
- };
- let (mut stream, timeout) = match stream_and_timeout(&lp) {
- Ok((stream, timeout)) => (stream, timeout),
- Err(_) => return NssResponse::Unavail,
- };
-
- let req = Request::NssAccountByUid(uid);
- stream
- .send(&req, timeout)
- .map(|r| match r {
- Response::NssAccount(passwd) => passwd
- .map(nsspasswd_from_passwd)
- .map(NssResponse::Success)
- .unwrap_or_else(|| NssResponse::NotFound),
- _ => NssResponse::NotFound,
- })
- .unwrap_or_else(|_| NssResponse::NotFound)
- }
-
- fn get_entry_by_name(name: String) -> NssResponse<NssPasswd> {
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return NssResponse::Unavail,
- };
- let (mut stream, timeout) = match stream_and_timeout(&lp) {
- Ok((stream, timeout)) => (stream, timeout),
- Err(_) => return NssResponse::Unavail,
- };
-
- let req = Request::NssAccountByName(name);
- stream
- .send(&req, timeout)
- .map(|r| match r {
- Response::NssAccount(passwd) => passwd
- .map(nsspasswd_from_passwd)
- .map(NssResponse::Success)
- .unwrap_or_else(|| NssResponse::NotFound),
- _ => NssResponse::NotFound,
- })
- .unwrap_or_else(|_| NssResponse::NotFound)
- }
-}
-
-struct HimmelblauGroup;
-libnss_group_hooks!(himmelblau, HimmelblauGroup);
-
-impl GroupHooks for HimmelblauGroup {
- fn get_all_entries() -> NssResponse<Vec<NssGroup>> {
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return NssResponse::Unavail,
- };
- let (mut stream, timeout) = match stream_and_timeout(&lp) {
- Ok((stream, timeout)) => (stream, timeout),
- Err(_) => return NssResponse::Unavail,
- };
-
- let req = Request::NssGroups;
- stream
- .send(&req, timeout)
- .map(|r| match r {
- Response::NssGroups(l) => {
- l.into_iter().map(|group| group.into()).collect()
- }
- _ => vec![],
- })
- .map(NssResponse::Success)
- .unwrap_or_else(|_| NssResponse::Success(vec![]))
- }
-
- fn get_entry_by_gid(gid: libc::gid_t) -> NssResponse<NssGroup> {
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return NssResponse::Unavail,
- };
- let (mut stream, timeout) = match stream_and_timeout(&lp) {
- Ok((stream, timeout)) => (stream, timeout),
- Err(_) => return NssResponse::Unavail,
- };
-
- let req = Request::NssGroupByGid(gid);
- stream
- .send(&req, timeout)
- .map(|r| match r {
- Response::NssGroup(group) => group
- .map(nssgroup_from_group)
- .map(NssResponse::Success)
- .unwrap_or_else(|| NssResponse::NotFound),
- _ => NssResponse::NotFound,
- })
- .unwrap_or_else(|_| NssResponse::NotFound)
- }
-
- fn get_entry_by_name(name: String) -> NssResponse<NssGroup> {
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return NssResponse::Unavail,
- };
- let (mut stream, timeout) = match stream_and_timeout(&lp) {
- Ok((stream, timeout)) => (stream, timeout),
- Err(_) => return NssResponse::Unavail,
- };
-
- let req = Request::NssGroupByName(name);
- stream
- .send(&req, timeout)
- .map(|r| match r {
- Response::NssGroup(group) => group
- .map(nssgroup_from_group)
- .map(NssResponse::Success)
- .unwrap_or_else(|| NssResponse::NotFound),
- _ => NssResponse::NotFound,
- })
- .unwrap_or_else(|_| NssResponse::NotFound)
- }
-}
-
-fn nsspasswd_from_passwd(passwd: Passwd) -> NssPasswd {
- passwd.into()
-}
-
-fn nssgroup_from_group(group: Group) -> NssGroup {
- group.into()
-}
+++ /dev/null
-[package]
-name = "ntstatus_gen"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Generated NTSTATUS Errors
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#[path = "../../../bin/default/libcli/util/ntstatus_gen.rs"]
-mod ntstatus_gen;
-pub use ntstatus_gen::*;
+++ /dev/null
-[package]
-name = "pam_himmelblau"
-links = "pam"
-
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[lib]
-name = "pam_himmelblau"
-crate-type = [ "cdylib" ]
-path = "src/lib.rs"
-
-[dependencies]
-libc = { workspace = true }
-dbg = { workspace = true }
-sock.workspace = true
-chelps.workspace = true
-param.workspace = true
-
-[build-dependencies]
-pkg-config = "0.3.30"
+++ /dev/null
-fn main() {
- println!("cargo:rerun-if-changed=build.rs");
- // ignore errors here since older versions of pam do not ship the pkg-config `pam.pc` file.
- // Not setting anything here will fall back on just blindly linking with `-lpam`,
- // which will work on environments with libpam.so, but no pkg-config file.
- let _ = pkg_config::Config::new()
- .atleast_version("1.3.0")
- .probe("pam");
- println!("cargo:rustc-env=LD_LIBRARY_PATH=../../bin/shared:../../bin/shared/private/");
-}
+++ /dev/null
-/*
- MIT License
-
- Copyright (c) 2015 TOZNY
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-
-#![deny(warnings)]
-#![warn(unused_extern_crates)]
-#![deny(clippy::todo)]
-#![deny(clippy::unimplemented)]
-// In this file, we do want to panic on these faults.
-// #![deny(clippy::unwrap_used)]
-// #![deny(clippy::expect_used)]
-#![deny(clippy::panic)]
-#![deny(clippy::unreachable)]
-#![deny(clippy::await_holding_lock)]
-#![deny(clippy::needless_pass_by_value)]
-#![deny(clippy::trivially_copy_pass_by_ref)]
-
-#[cfg(target_family = "unix")]
-mod pam;
-
-// pub use needs to be here so it'll compile and export all the things
-#[cfg(target_family = "unix")]
-pub use crate::pam::*;
+++ /dev/null
-/*
- MIT License
-
- Copyright (c) 2015 TOZNY
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-use libc::{c_int, c_uint};
-
-pub type PamFlag = c_uint;
-pub type PamItemType = c_int;
-pub type PamMessageStyle = c_int;
-pub type AlwaysZero = c_int;
-
-// The Linux-PAM flags
-// see /usr/include/security/_pam_types.h
-pub const _PAM_SILENT: PamFlag = 0x8000;
-pub const _PAM_DISALLOW_NULL_AUTHTOK: PamFlag = 0x0001;
-pub const _PAM_ESTABLISH_CRED: PamFlag = 0x0002;
-pub const _PAM_DELETE_CRED: PamFlag = 0x0004;
-pub const _PAM_REINITIALIZE_CRED: PamFlag = 0x0008;
-pub const _PAM_REFRESH_CRED: PamFlag = 0x0010;
-pub const _PAM_CHANGE_EXPIRED_AUTHTOK: PamFlag = 0x0020;
-
-// The Linux-PAM item types
-// see /usr/include/security/_pam_types.h
-/// The service name
-pub const PAM_SERVICE: PamItemType = 1;
-/// The user name
-pub const PAM_USER: PamItemType = 2;
-/// The tty name
-pub const PAM_TTY: PamItemType = 3;
-/// The remote host name
-pub const PAM_RHOST: PamItemType = 4;
-/// The pam_conv structure
-pub const PAM_CONV: PamItemType = 5;
-/// The authentication token (password)
-pub const PAM_AUTHTOK: PamItemType = 6;
-/// The old authentication token
-pub const PAM_OLDAUTHTOK: PamItemType = 7;
-/// The remote user name
-pub const PAM_RUSER: PamItemType = 8;
-/// the prompt for getting a username
-pub const PAM_USER_PROMPT: PamItemType = 9;
-/* Linux-PAM :extensionsPamItemType = */
-/// app supplied function to override failure delays
-pub const _PAM_FAIL_DELAY: PamItemType = 10;
-/// X :display name
-pub const _PAM_XDISPLAY: PamItemType = 11;
-/// X :server authentication data
-pub const _PAM_XAUTHDATA: PamItemType = 12;
-/// The type for pam_get_authtok
-pub const _PAM_AUTHTOK_TYPE: PamItemType = 13;
-
-// Message styles
-pub const PAM_PROMPT_ECHO_OFF: PamMessageStyle = 1;
-pub const PAM_PROMPT_ECHO_ON: PamMessageStyle = 2;
-pub const PAM_ERROR_MSG: PamMessageStyle = 3;
-pub const PAM_TEXT_INFO: PamMessageStyle = 4;
-/// yes/no/maybe conditionals
-pub const _PAM_RADIO_TYPE: PamMessageStyle = 5;
-pub const _PAM_BINARY_PROMPT: PamMessageStyle = 7;
-
-// The Linux-PAM return values
-// see /usr/include/security/_pam_types.h
-#[allow(non_camel_case_types, dead_code)]
-#[derive(Debug, PartialEq)]
-#[repr(C)]
-pub enum PamResultCode {
- PAM_SUCCESS = 0,
- PAM_OPEN_ERR = 1,
- PAM_SYMBOL_ERR = 2,
- PAM_SERVICE_ERR = 3,
- PAM_SYSTEM_ERR = 4,
- PAM_BUF_ERR = 5,
- PAM_PERM_DENIED = 6,
- PAM_AUTH_ERR = 7,
- PAM_CRED_INSUFFICIENT = 8,
- PAM_AUTHINFO_UNAVAIL = 9,
- PAM_USER_UNKNOWN = 10,
- PAM_MAXTRIES = 11,
- PAM_NEW_AUTHTOK_REQD = 12,
- PAM_ACCT_EXPIRED = 13,
- PAM_SESSION_ERR = 14,
- PAM_CRED_UNAVAIL = 15,
- PAM_CRED_EXPIRED = 16,
- PAM_CRED_ERR = 17,
- PAM_NO_MODULE_DATA = 18,
- PAM_CONV_ERR = 19,
- PAM_AUTHTOK_ERR = 20,
- PAM_AUTHTOK_RECOVERY_ERR = 21,
- PAM_AUTHTOK_LOCK_BUSY = 22,
- PAM_AUTHTOK_DISABLE_AGING = 23,
- PAM_TRY_AGAIN = 24,
- PAM_IGNORE = 25,
- PAM_ABORT = 26,
- PAM_AUTHTOK_EXPIRED = 27,
- PAM_MODULE_UNKNOWN = 28,
- PAM_BAD_ITEM = 29,
- PAM_CONV_AGAIN = 30,
- PAM_INCOMPLETE = 31,
-}
+++ /dev/null
-/*
- MIT License
-
- Copyright (c) 2015 TOZNY
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-use std::ffi::{CStr, CString};
-use std::ptr;
-
-use libc::{c_char, c_int};
-
-use crate::pam::constants::{PamResultCode, *};
-use crate::pam::module::{PamItem, PamResult};
-
-#[allow(missing_copy_implementations)]
-pub enum AppDataPtr {}
-
-#[repr(C)]
-struct PamMessage {
- msg_style: PamMessageStyle,
- msg: *const c_char,
-}
-
-#[repr(C)]
-struct PamResponse {
- resp: *const c_char,
- resp_retcode: AlwaysZero,
-}
-
-/// `PamConv` acts as a channel for communicating with user.
-///
-/// Communication is mediated by the pam client (the application that invoked
-/// pam). Messages sent will be relayed to the user by the client, and response
-/// will be relayed back.
-#[repr(C)]
-pub struct PamConv {
- conv: extern "C" fn(
- num_msg: c_int,
- pam_message: &&PamMessage,
- pam_response: &mut *const PamResponse,
- appdata_ptr: *const AppDataPtr,
- ) -> PamResultCode,
- appdata_ptr: *const AppDataPtr,
-}
-
-impl PamConv {
- /// Sends a message to the pam client.
- ///
- /// This will typically result in the user seeing a message or a prompt.
- /// There are several message styles available:
- ///
- /// - PAM_PROMPT_ECHO_OFF
- /// - PAM_PROMPT_ECHO_ON
- /// - PAM_ERROR_MSG
- /// - PAM_TEXT_INFO
- /// - PAM_RADIO_TYPE
- /// - PAM_BINARY_PROMPT
- ///
- /// Note that the user experience will depend on how the client implements
- /// these message styles - and not all applications implement all message
- /// styles.
- pub fn send(
- &self,
- style: PamMessageStyle,
- msg: &str,
- ) -> PamResult<Option<String>> {
- let mut resp_ptr: *const PamResponse = ptr::null();
- let msg_cstr = CString::new(msg).unwrap();
- let msg = PamMessage {
- msg_style: style,
- msg: msg_cstr.as_ptr(),
- };
-
- let ret = (self.conv)(1, &&msg, &mut resp_ptr, self.appdata_ptr);
-
- if PamResultCode::PAM_SUCCESS == ret {
- // PamResponse.resp is null for styles that don't return user input like PAM_TEXT_INFO
- let response = unsafe { (*resp_ptr).resp };
- if response.is_null() {
- Ok(None)
- } else {
- let bytes = unsafe { CStr::from_ptr(response).to_bytes() };
- Ok(String::from_utf8(bytes.to_vec()).ok())
- }
- } else {
- Err(ret)
- }
- }
-}
-
-impl PamItem for PamConv {
- fn item_type() -> PamItemType {
- PAM_CONV
- }
-}
+++ /dev/null
-/*
- MIT License
-
- Copyright (c) 2015 TOZNY
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-use crate::pam::constants::{
- PamItemType, PAM_AUTHTOK, PAM_OLDAUTHTOK, PAM_RHOST, PAM_RUSER,
- PAM_SERVICE, PAM_TTY, PAM_USER, PAM_USER_PROMPT,
-};
-pub use crate::pam::conv::PamConv;
-use crate::pam::module::PamItem;
-
-#[allow(dead_code)]
-pub struct PamService {}
-
-impl PamItem for PamService {
- fn item_type() -> PamItemType {
- PAM_SERVICE
- }
-}
-
-#[allow(dead_code)]
-pub struct PamUser {}
-
-impl PamItem for PamUser {
- fn item_type() -> PamItemType {
- PAM_USER
- }
-}
-
-#[allow(dead_code)]
-pub struct PamUserPrompt {}
-
-impl PamItem for PamUserPrompt {
- fn item_type() -> PamItemType {
- PAM_USER_PROMPT
- }
-}
-
-#[allow(dead_code)]
-pub struct PamTty {}
-
-impl PamItem for PamTty {
- fn item_type() -> PamItemType {
- PAM_TTY
- }
-}
-
-#[allow(dead_code)]
-pub struct PamRUser {}
-
-impl PamItem for PamRUser {
- fn item_type() -> PamItemType {
- PAM_RUSER
- }
-}
-
-#[allow(dead_code)]
-pub struct PamRHost {}
-
-impl PamItem for PamRHost {
- fn item_type() -> PamItemType {
- PAM_RHOST
- }
-}
-
-#[allow(dead_code)]
-pub struct PamAuthTok {}
-
-impl PamItem for PamAuthTok {
- fn item_type() -> PamItemType {
- PAM_AUTHTOK
- }
-}
-
-#[allow(dead_code)]
-pub struct PamOldAuthTok {}
-
-impl PamItem for PamOldAuthTok {
- fn item_type() -> PamItemType {
- PAM_OLDAUTHTOK
- }
-}
+++ /dev/null
-/*
- MIT License
-
- Copyright (c) 2015 TOZNY
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-/// Macro to generate the `extern "C"` entrypoint bindings needed by PAM
-///
-/// You can call `pam_hooks!(SomeType);` for any type that implements `PamHooks`
-///
-/// ## Examples:
-///
-/// Here is full example of a PAM module that would authenticate and authorize everybody:
-///
-/// ```
-/// #[macro_use]
-/// extern crate pam;
-///
-/// use pam::constants::{PamFlag, PamResultCode};
-/// use pam::module::{PamHandle, PamHooks};
-/// use std::ffi::CStr;
-///
-/// # fn main() {}
-/// struct MyPamModule;
-/// pam_hooks!(MyPamModule);
-///
-/// impl PamHooks for MyPamModule {
-/// fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-/// println!("Everybody is authenticated!");
-/// PamResultCode::PAM_SUCCESS
-/// }
-///
-/// fn acct_mgmt(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-/// println!("Everybody is authorized!");
-/// PamResultCode::PAM_SUCCESS
-/// }
-/// }
-/// ```
-#[macro_export]
-macro_rules! pam_hooks {
- ($ident:ident) => {
- pub use self::pam_hooks_scope::*;
- mod pam_hooks_scope {
- use std::ffi::CStr;
- use std::os::raw::{c_char, c_int};
-
- use $crate::pam::constants::{PamFlag, PamResultCode};
- use $crate::pam::module::{PamHandle, PamHooks};
-
- fn extract_argv<'a>(argc: c_int, argv: *const *const c_char) -> Vec<&'a CStr> {
- (0..argc)
- .map(|o| unsafe { CStr::from_ptr(*argv.offset(o as isize) as *const c_char) })
- .collect()
- }
-
- #[no_mangle]
- pub extern "C" fn pam_sm_acct_mgmt(
- pamh: &PamHandle,
- flags: PamFlag,
- argc: c_int,
- argv: *const *const c_char,
- ) -> PamResultCode {
- let args = extract_argv(argc, argv);
- super::$ident::acct_mgmt(pamh, args, flags)
- }
-
- #[no_mangle]
- pub extern "C" fn pam_sm_authenticate(
- pamh: &PamHandle,
- flags: PamFlag,
- argc: c_int,
- argv: *const *const c_char,
- ) -> PamResultCode {
- let args = extract_argv(argc, argv);
- super::$ident::sm_authenticate(pamh, args, flags)
- }
-
- #[no_mangle]
- pub extern "C" fn pam_sm_chauthtok(
- pamh: &PamHandle,
- flags: PamFlag,
- argc: c_int,
- argv: *const *const c_char,
- ) -> PamResultCode {
- let args = extract_argv(argc, argv);
- super::$ident::sm_chauthtok(pamh, args, flags)
- }
-
- #[no_mangle]
- pub extern "C" fn pam_sm_close_session(
- pamh: &PamHandle,
- flags: PamFlag,
- argc: c_int,
- argv: *const *const c_char,
- ) -> PamResultCode {
- let args = extract_argv(argc, argv);
- super::$ident::sm_close_session(pamh, args, flags)
- }
-
- #[no_mangle]
- pub extern "C" fn pam_sm_open_session(
- pamh: &PamHandle,
- flags: PamFlag,
- argc: c_int,
- argv: *const *const c_char,
- ) -> PamResultCode {
- let args = extract_argv(argc, argv);
- super::$ident::sm_open_session(pamh, args, flags)
- }
-
- #[no_mangle]
- pub extern "C" fn pam_sm_setcred(
- pamh: &PamHandle,
- flags: PamFlag,
- argc: c_int,
- argv: *const *const c_char,
- ) -> PamResultCode {
- let args = extract_argv(argc, argv);
- super::$ident::sm_setcred(pamh, args, flags)
- }
- }
- };
-}
+++ /dev/null
-/*
- MIT License
-
- Copyright (c) 2015 TOZNY
- Copyright (c) 2020 William Brown <william@blackhats.net.au>
- Copyright (c) 2024 David Mulder <dmulder@samba.org>
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-//! Interface to the pluggable authentication module framework (PAM).
-//!
-//! The goal of this library is to provide a type-safe API that can be used to
-//! interact with PAM. The library is incomplete - currently it supports
-//! a subset of functions for use in a pam authentication module. A pam module
-//! is a shared library that is invoked to authenticate a user, or to perform
-//! other functions.
-//!
-//! For general information on writing pam modules, see
-//! [The Linux-PAM Module Writers' Guide][module-guide]
-//!
-//! [module-guide]: http://www.linux-pam.org/Linux-PAM-html/Linux-PAM_MWG.html
-//!
-//! A typical authentication module will define an external function called
-//! `pam_sm_authenticate()`, which will use functions in this library to
-//! interrogate the program that requested authentication for more information,
-//! and to render a result. For a working example that uses this library, see
-//! [toznyauth-pam][].
-//!
-//! [toznyauth-pam]: https://github.com/tozny/toznyauth-pam
-//!
-//! Note that constants that are normally read from pam header files are
-//! hard-coded in the `constants` module. The values there are taken from
-//! a Linux system. That means that it might take some work to get this library
-//! to work on other platforms.
-
-pub mod constants;
-pub mod conv;
-pub mod items;
-#[doc(hidden)]
-pub mod macros;
-pub mod module;
-
-use std::collections::BTreeSet;
-use std::convert::TryFrom;
-use std::ffi::CStr;
-
-use dbg::*;
-use param::LoadParm;
-use sock::{
- stream_and_timeout, PamAuthRequest, PamAuthResponse, Request, Response,
-};
-
-use crate::pam::constants::*;
-use crate::pam::conv::PamConv;
-use crate::pam::module::{PamHandle, PamHooks};
-use crate::pam_hooks;
-use constants::PamResultCode;
-
-use std::thread;
-use std::time::Duration;
-
-fn install_subscriber(lp: &LoadParm, debug: bool) {
- debuglevel_set!(if debug { DBGLVL_DEBUG } else { DBGLVL_ERR });
- match lp.logfile() {
- Ok(Some(logfile)) => {
- debug_set_logfile(&logfile);
- setup_logging(env!("CARGO_PKG_NAME"), DEBUG_FILE)
- }
- _ => setup_logging(env!("CARGO_PKG_NAME"), DEBUG_STDOUT),
- }
-}
-
-#[derive(Debug)]
-struct Options {
- debug: bool,
- use_first_pass: bool,
- ignore_unknown_user: bool,
-}
-
-impl TryFrom<&Vec<&CStr>> for Options {
- type Error = ();
-
- fn try_from(args: &Vec<&CStr>) -> Result<Self, Self::Error> {
- let opts: Result<BTreeSet<&str>, _> =
- args.iter().map(|cs| cs.to_str()).collect();
- let gopts = match opts {
- Ok(o) => o,
- Err(e) => {
- println!("Error in module args -> {:?}", e);
- return Err(());
- }
- };
-
- Ok(Options {
- debug: gopts.contains("debug"),
- use_first_pass: gopts.contains("use_first_pass"),
- ignore_unknown_user: gopts.contains("ignore_unknown_user"),
- })
- }
-}
-
-pub struct PamHimmelblau;
-
-pam_hooks!(PamHimmelblau);
-
-macro_rules! match_sm_auth_client_response {
- ($expr:expr, $opts:ident, $conv:ident, $req:ident, $authtok:ident, $($pat:pat => $result:expr),*) => {
- match $expr {
- Ok(r) => match r {
- $($pat => $result),*
- Response::PamAuthStepResponse(PamAuthResponse::Success) => {
- return PamResultCode::PAM_SUCCESS;
- }
- Response::PamAuthStepResponse(PamAuthResponse::Denied) => {
- return PamResultCode::PAM_AUTH_ERR;
- }
- Response::PamAuthStepResponse(PamAuthResponse::Unknown) => {
- if $opts.ignore_unknown_user {
- return PamResultCode::PAM_IGNORE;
- } else {
- return PamResultCode::PAM_USER_UNKNOWN;
- }
- }
- Response::PamAuthStepResponse(PamAuthResponse::SetupPin {
- msg,
- }) => {
- match $conv.send(PAM_TEXT_INFO, &msg) {
- Ok(_) => {}
- Err(err) => {
- if $opts.debug {
- println!("Message prompt failed");
- }
- return err;
- }
- }
-
- let mut pin;
- let mut confirm;
- loop {
- pin = match $conv.send(PAM_PROMPT_ECHO_OFF, "New PIN: ") {
- Ok(password) => match password {
- Some(cred) => cred,
- None => {
- DBG_DEBUG!("no pin");
- return PamResultCode::PAM_CRED_INSUFFICIENT;
- }
- },
- Err(err) => {
- DBG_DEBUG!("unable to get pin");
- return err;
- }
- };
-
- confirm = match $conv.send(PAM_PROMPT_ECHO_OFF, "Confirm PIN: ") {
- Ok(password) => match password {
- Some(cred) => cred,
- None => {
- DBG_DEBUG!("no confirmation pin");
- return PamResultCode::PAM_CRED_INSUFFICIENT;
- }
- },
- Err(err) => {
- DBG_DEBUG!("unable to get confirmation pin");
- return err;
- }
- };
-
- if pin == confirm {
- break;
- } else {
- match $conv.send(PAM_TEXT_INFO, "Inputs did not match. Try again.") {
- Ok(_) => {}
- Err(err) => {
- if $opts.debug {
- println!("Message prompt failed");
- }
- return err;
- }
- }
- }
- }
-
- // Now setup the request for the next loop.
- $req = Request::PamAuthenticateStep(PamAuthRequest::SetupPin {
- pin,
- });
- continue;
- },
- Response::PamAuthStepResponse(PamAuthResponse::Pin) => {
- let mut consume_authtok = None;
- // Swap the authtok out with a None, so it can only be consumed once.
- // If it's already been swapped, we are just swapping two null pointers
- // here effectively.
- std::mem::swap(&mut $authtok, &mut consume_authtok);
- let cred = if let Some(cred) = consume_authtok {
- cred
- } else {
- match $conv.send(PAM_PROMPT_ECHO_OFF, "PIN: ") {
- Ok(password) => match password {
- Some(cred) => cred,
- None => {
- DBG_DEBUG!("no pin");
- return PamResultCode::PAM_CRED_INSUFFICIENT;
- }
- },
- Err(err) => {
- DBG_DEBUG!("unable to get pin");
- return err;
- }
- }
- };
-
- // Now setup the request for the next loop.
- $req = Request::PamAuthenticateStep(PamAuthRequest::Pin { pin: cred });
- continue;
- }
- _ => {
- // unexpected response.
- DBG_ERR!("PAM_IGNORE, unexpected resolver response: {:?}", r);
- return PamResultCode::PAM_IGNORE;
- }
- },
- Err(e) => {
- DBG_ERR!("PAM_IGNORE: {:?}", e);
- return PamResultCode::PAM_IGNORE;
- }
- }
- }
-}
-
-impl PamHooks for PamHimmelblau {
- fn acct_mgmt(
- pamh: &PamHandle,
- args: Vec<&CStr>,
- _flags: PamFlag,
- ) -> PamResultCode {
- let opts = match Options::try_from(&args) {
- Ok(o) => o,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- install_subscriber(&lp, opts.debug);
-
- let tty = pamh.get_tty();
- let rhost = pamh.get_rhost();
-
- DBG_DEBUG!("{:?} {:?} {:?} {:?} acct_mgmt", args, opts, tty, rhost);
-
- let account_id = match pamh.get_user(None) {
- Ok(aid) => aid,
- Err(e) => {
- DBG_ERR!("get_user: {:?}", e);
- return e;
- }
- };
-
- let req = Request::PamAccountAllowed(account_id);
-
- let (mut stream, timeout) = match stream_and_timeout(&lp) {
- Ok(res) => res,
- Err(e) => {
- DBG_ERR!("Error stream_and_timeout: {:?}", e);
- return PamResultCode::PAM_SERVICE_ERR;
- }
- };
-
- match stream.send(&req, timeout) {
- Ok(r) => match r {
- Response::PamStatus(Some(true)) => {
- DBG_DEBUG!("PamResultCode::PAM_SUCCESS");
- PamResultCode::PAM_SUCCESS
- }
- Response::PamStatus(Some(false)) => {
- DBG_DEBUG!("PamResultCode::PAM_AUTH_ERR");
- PamResultCode::PAM_AUTH_ERR
- }
- Response::PamStatus(None) => {
- if opts.ignore_unknown_user {
- DBG_DEBUG!("PamResultCode::PAM_IGNORE");
- PamResultCode::PAM_IGNORE
- } else {
- DBG_DEBUG!("PamResultCode::PAM_USER_UNKNOWN");
- PamResultCode::PAM_USER_UNKNOWN
- }
- }
- _ => {
- // unexpected response.
- DBG_ERR!(
- "PAM_IGNORE, unexpected resolver response: {:?}",
- r
- );
- PamResultCode::PAM_IGNORE
- }
- },
- Err(e) => {
- DBG_ERR!("PamResultCode::PAM_IGNORE: {:?}", e);
- PamResultCode::PAM_IGNORE
- }
- }
- }
-
- fn sm_authenticate(
- pamh: &PamHandle,
- args: Vec<&CStr>,
- _flags: PamFlag,
- ) -> PamResultCode {
- let opts = match Options::try_from(&args) {
- Ok(o) => o,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- install_subscriber(&lp, opts.debug);
-
- let tty = pamh.get_tty();
- let rhost = pamh.get_rhost();
-
- DBG_DEBUG!(
- "{:?} {:?} {:?} {:?} sm_authenticate",
- args,
- opts,
- tty,
- rhost
- );
-
- let account_id = match pamh.get_user(None) {
- Ok(aid) => aid,
- Err(e) => {
- DBG_ERR!("get_user: {:?}", e);
- return e;
- }
- };
-
- let (mut stream, timeout) = match stream_and_timeout(&lp) {
- Ok(res) => res,
- Err(e) => {
- DBG_ERR!("Error stream_and_timeout: {:?}", e);
- return PamResultCode::PAM_SERVICE_ERR;
- }
- };
-
- // Later we may need to move this to a function and call it as a oneshot for auth methods
- // that don't require any authtoks at all. For example, imagine a user authed and they
- // needed to follow a URL to continue. In that case, they would fail here because they
- // didn't enter an authtok that they didn't need!
- let mut authtok = match pamh.get_authtok() {
- Ok(Some(v)) => Some(v),
- Ok(None) => {
- if opts.use_first_pass {
- DBG_DEBUG!("Don't have an authtok, returning PAM_AUTH_ERR");
- return PamResultCode::PAM_AUTH_ERR;
- }
- None
- }
- Err(e) => {
- DBG_ERR!("get_authtok: {:?}", e);
- return e;
- }
- };
-
- let conv = match pamh.get_item::<PamConv>() {
- Ok(conv) => conv,
- Err(e) => {
- DBG_ERR!("pam_conv: {:?}", e);
- return e;
- }
- };
-
- let mut req = Request::PamAuthenticateInit(account_id);
-
- loop {
- match_sm_auth_client_response!(stream.send(&req, timeout), opts, conv, req, authtok,
- Response::PamAuthStepResponse(PamAuthResponse::Password) => {
- let mut consume_authtok = None;
- // Swap the authtok out with a None, so it can only be consumed once.
- // If it's already been swapped, we are just swapping two null pointers
- // here effectively.
- std::mem::swap(&mut authtok, &mut consume_authtok);
- let cred = if let Some(cred) = consume_authtok {
- cred
- } else {
- match conv.send(PAM_PROMPT_ECHO_OFF, "Password: ") {
- Ok(password) => match password {
- Some(cred) => cred,
- None => {
- DBG_DEBUG!("no password");
- return PamResultCode::PAM_CRED_INSUFFICIENT;
- }
- },
- Err(err) => {
- DBG_DEBUG!("unable to get password");
- return err;
- }
- }
- };
-
- // Now setup the request for the next loop.
- req = Request::PamAuthenticateStep(PamAuthRequest::Password { cred });
- continue;
- },
- Response::PamAuthStepResponse(PamAuthResponse::MFACode {
- msg,
- }) => {
- match conv.send(PAM_TEXT_INFO, &msg) {
- Ok(_) => {}
- Err(err) => {
- if opts.debug {
- println!("Message prompt failed");
- }
- return err;
- }
- }
- let cred = match conv.send(PAM_PROMPT_ECHO_OFF, "Code: ") {
- Ok(password) => match password {
- Some(cred) => cred,
- None => {
- DBG_DEBUG!("no mfa code");
- return PamResultCode::PAM_CRED_INSUFFICIENT;
- }
- },
- Err(err) => {
- DBG_DEBUG!("unable to get mfa code");
- return err;
- }
- };
-
- // Now setup the request for the next loop.
- req = Request::PamAuthenticateStep(PamAuthRequest::MFACode {
- cred,
- });
- continue;
- },
- Response::PamAuthStepResponse(PamAuthResponse::MFAPoll {
- msg,
- polling_interval,
- }) => {
- match conv.send(PAM_TEXT_INFO, &msg) {
- Ok(_) => {}
- Err(err) => {
- if opts.debug {
- println!("Message prompt failed");
- }
- return err;
- }
- }
-
- // Necessary because of OpenSSH bug
- // https://bugzilla.mindrot.org/show_bug.cgi?id=2876 -
- // PAM_TEXT_INFO and PAM_ERROR_MSG conversation not
- // honoured during PAM authentication
- let _ = conv.send(PAM_PROMPT_ECHO_OFF, "Press enter to continue");
-
- let mut poll_attempt = 0;
- req = Request::PamAuthenticateStep(
- PamAuthRequest::MFAPoll { poll_attempt }
- );
- loop {
- thread::sleep(Duration::from_secs(polling_interval.into()));
-
- match_sm_auth_client_response!(
- stream.send(&req, timeout), opts, conv, req, authtok,
- Response::PamAuthStepResponse(
- PamAuthResponse::MFAPollWait,
- ) => {
- // Continue polling if the daemon says to wait
- poll_attempt += 1;
- req = Request::PamAuthenticateStep(
- PamAuthRequest::MFAPoll { poll_attempt }
- );
- continue;
- }
- );
- }
- }
- );
- } // while true, continue calling PamAuthenticateStep until we get a decision.
- }
-
- fn sm_chauthtok(
- _pamh: &PamHandle,
- args: Vec<&CStr>,
- _flags: PamFlag,
- ) -> PamResultCode {
- let opts = match Options::try_from(&args) {
- Ok(o) => o,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- install_subscriber(&lp, opts.debug);
-
- DBG_DEBUG!("{:?} {:?} sm_chauthtok", args, opts);
-
- PamResultCode::PAM_IGNORE
- }
-
- fn sm_close_session(
- _pamh: &PamHandle,
- args: Vec<&CStr>,
- _flags: PamFlag,
- ) -> PamResultCode {
- let opts = match Options::try_from(&args) {
- Ok(o) => o,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- install_subscriber(&lp, opts.debug);
-
- DBG_DEBUG!("{:?} {:?} sm_close_session", args, opts);
-
- PamResultCode::PAM_SUCCESS
- }
-
- fn sm_open_session(
- pamh: &PamHandle,
- args: Vec<&CStr>,
- _flags: PamFlag,
- ) -> PamResultCode {
- let opts = match Options::try_from(&args) {
- Ok(o) => o,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- install_subscriber(&lp, opts.debug);
-
- DBG_DEBUG!("{:?} {:?} sm_open_session", args, opts);
-
- let account_id = match pamh.get_user(None) {
- Ok(aid) => aid,
- Err(e) => {
- DBG_ERR!("get_user: {:?}", e);
- return e;
- }
- };
-
- let req = Request::PamAccountBeginSession(account_id);
-
- let (mut stream, timeout) = match stream_and_timeout(&lp) {
- Ok(res) => res,
- Err(e) => {
- DBG_ERR!("Error stream_and_timeout: {:?}", e);
- return PamResultCode::PAM_SERVICE_ERR;
- }
- };
-
- match stream.send(&req, timeout) {
- Ok(Response::Success) => PamResultCode::PAM_SUCCESS,
- other => {
- DBG_DEBUG!("PAM_IGNORE: {:?}", other);
- PamResultCode::PAM_IGNORE
- }
- }
- }
-
- fn sm_setcred(
- _pamh: &PamHandle,
- args: Vec<&CStr>,
- _flags: PamFlag,
- ) -> PamResultCode {
- let opts = match Options::try_from(&args) {
- Ok(o) => o,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- let lp = match LoadParm::new(None) {
- Ok(lp) => lp,
- Err(_) => return PamResultCode::PAM_SERVICE_ERR,
- };
-
- install_subscriber(&lp, opts.debug);
-
- DBG_DEBUG!("{:?} {:?} sm_setcred", args, opts);
-
- PamResultCode::PAM_SUCCESS
- }
-}
+++ /dev/null
-/*
- MIT License
-
- Copyright (c) 2015 TOZNY
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-*/
-//! Functions for use in pam modules.
-
-use std::ffi::{CStr, CString};
-use std::{mem, ptr};
-
-use libc::c_char;
-
-use crate::pam::constants::{
- PamFlag, PamItemType, PamResultCode, PAM_AUTHTOK, PAM_RHOST, PAM_TTY,
-};
-
-/// Opaque type, used as a pointer when making pam API calls.
-///
-/// A module is invoked via an external function such as `pam_sm_authenticate`.
-/// Such a call provides a pam handle pointer. The same pointer should be given
-/// as an argument when making API calls.
-#[allow(missing_copy_implementations)]
-pub enum PamHandle {}
-
-#[allow(missing_copy_implementations)]
-enum PamItemT {}
-
-#[allow(missing_copy_implementations)]
-pub enum PamDataT {}
-
-#[link(name = "pam")]
-extern "C" {
- fn pam_get_data(
- pamh: *const PamHandle,
- module_data_name: *const c_char,
- data: &mut *const PamDataT,
- ) -> PamResultCode;
-
- fn pam_set_data(
- pamh: *const PamHandle,
- module_data_name: *const c_char,
- data: *mut PamDataT,
- cleanup: unsafe extern "C" fn(
- pamh: *const PamHandle,
- data: *mut PamDataT,
- error_status: PamResultCode,
- ),
- ) -> PamResultCode;
-
- fn pam_get_item(
- pamh: *const PamHandle,
- item_type: PamItemType,
- item: &mut *const PamItemT,
- ) -> PamResultCode;
-
- fn pam_set_item(
- pamh: *mut PamHandle,
- item_type: PamItemType,
- item: &PamItemT,
- ) -> PamResultCode;
-
- fn pam_get_user(
- pamh: *const PamHandle,
- user: &*mut c_char,
- prompt: *const c_char,
- ) -> PamResultCode;
-}
-
-/// # Safety
-///
-/// We're doing what we can for this one, but it's FFI.
-pub unsafe extern "C" fn cleanup<T>(
- _: *const PamHandle,
- c_data: *mut PamDataT,
- _: PamResultCode,
-) {
- let c_data = Box::from_raw(c_data);
- let data: Box<T> = mem::transmute(c_data);
- mem::drop(data);
-}
-
-pub type PamResult<T> = Result<T, PamResultCode>;
-
-/// # Safety
-///
-/// Type-level mapping for safely retrieving values with `get_item`.
-///
-/// See `pam_get_item` in
-/// <http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html>
-pub trait PamItem {
- /// Maps a Rust type to a pam constant.
- ///
- /// For example, the type PamConv maps to the constant PAM_CONV. The pam
- /// API contract specifies that when the API function `pam_get_item` is
- /// called with the constant PAM_CONV, it will return a value of type
- /// `PamConv`.
- fn item_type() -> PamItemType;
-}
-
-impl PamHandle {
- /// # Safety
- ///
- /// Gets some value, identified by `key`, that has been set by the module
- /// previously.
- ///
- /// See `pam_get_data` in
- /// <http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html>
- pub unsafe fn get_data<'a, T>(&'a self, key: &str) -> PamResult<&'a T> {
- let c_key = CString::new(key).unwrap();
- let mut ptr: *const PamDataT = ptr::null();
- let res = pam_get_data(self, c_key.as_ptr(), &mut ptr);
- if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
- let typed_ptr: *const T = ptr as *const T;
- let data: &T = &*typed_ptr;
- Ok(data)
- } else {
- Err(res)
- }
- }
-
- /// Stores a value that can be retrieved later with `get_data`. The value lives
- /// as long as the current pam cycle.
- ///
- /// See `pam_set_data` in
- /// <http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html>
- pub fn set_data<T>(&self, key: &str, data: Box<T>) -> PamResult<()> {
- let c_key = CString::new(key).unwrap();
- let res = unsafe {
- let c_data: Box<PamDataT> = mem::transmute(data);
- let c_data = Box::into_raw(c_data);
- pam_set_data(self, c_key.as_ptr(), c_data, cleanup::<T>)
- };
- if PamResultCode::PAM_SUCCESS == res {
- Ok(())
- } else {
- Err(res)
- }
- }
-
- /// Retrieves a value that has been set, possibly by the pam client. This is
- /// particularly useful for getting a `PamConv` reference.
- ///
- /// See `pam_get_item` in
- /// <http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html>
- pub fn get_item<'a, T: PamItem>(&self) -> PamResult<&'a T> {
- let mut ptr: *const PamItemT = ptr::null();
- let (res, item) = unsafe {
- let r = pam_get_item(self, T::item_type(), &mut ptr);
- let typed_ptr: *const T = ptr as *const T;
- let t: &T = &*typed_ptr;
- (r, t)
- };
- if PamResultCode::PAM_SUCCESS == res {
- Ok(item)
- } else {
- Err(res)
- }
- }
-
- /// Sets a value in the pam context. The value can be retrieved using
- /// `get_item`.
- ///
- /// Note that all items are strings, except `PAM_CONV` and `PAM_FAIL_DELAY`.
- ///
- /// See `pam_set_item` in
- /// <http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html>
- pub fn set_item_str<T: PamItem>(&mut self, item: &str) -> PamResult<()> {
- let c_item = CString::new(item).unwrap();
-
- let res = unsafe {
- pam_set_item(
- self,
- T::item_type(),
- // unwrapping is okay here, as c_item will not be a NULL
- // pointer
- (c_item.as_ptr() as *const PamItemT).as_ref().unwrap(),
- )
- };
- if PamResultCode::PAM_SUCCESS == res {
- Ok(())
- } else {
- Err(res)
- }
- }
-
- /// Retrieves the name of the user who is authenticating or logging in.
- ///
- /// This is really a specialization of `get_item`.
- ///
- /// See `pam_get_user` in
- /// <http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html>
- pub fn get_user(&self, prompt: Option<&str>) -> PamResult<String> {
- let ptr: *mut c_char = ptr::null_mut();
- let res = match prompt {
- Some(p) => {
- let c_prompt = CString::new(p).unwrap();
- unsafe { pam_get_user(self, &ptr, c_prompt.as_ptr()) }
- }
- None => unsafe { pam_get_user(self, &ptr, ptr::null()) },
- };
-
- if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
- let const_ptr = ptr as *const c_char;
- let bytes = unsafe { CStr::from_ptr(const_ptr).to_bytes() };
- String::from_utf8(bytes.to_vec())
- .map_err(|_| PamResultCode::PAM_CONV_ERR)
- } else {
- Err(res)
- }
- }
-
- pub fn get_authtok(&self) -> PamResult<Option<String>> {
- let mut ptr: *const PamItemT = ptr::null();
- let (res, item) = unsafe {
- let r = pam_get_item(self, PAM_AUTHTOK, &mut ptr);
- let t = if PamResultCode::PAM_SUCCESS == r && !ptr.is_null() {
- let typed_ptr: *const c_char = ptr as *const c_char;
- Some(CStr::from_ptr(typed_ptr).to_string_lossy().into_owned())
- } else {
- None
- };
- (r, t)
- };
- if PamResultCode::PAM_SUCCESS == res {
- Ok(item)
- } else {
- Err(res)
- }
- }
-
- pub fn get_tty(&self) -> PamResult<Option<String>> {
- let mut ptr: *const PamItemT = ptr::null();
- let (res, item) = unsafe {
- let r = pam_get_item(self, PAM_TTY, &mut ptr);
- let t = if PamResultCode::PAM_SUCCESS == r && !ptr.is_null() {
- let typed_ptr: *const c_char = ptr as *const c_char;
- Some(CStr::from_ptr(typed_ptr).to_string_lossy().into_owned())
- } else {
- None
- };
- (r, t)
- };
- if PamResultCode::PAM_SUCCESS == res {
- Ok(item)
- } else {
- Err(res)
- }
- }
-
- pub fn get_rhost(&self) -> PamResult<Option<String>> {
- let mut ptr: *const PamItemT = ptr::null();
- let (res, item) = unsafe {
- let r = pam_get_item(self, PAM_RHOST, &mut ptr);
- let t = if PamResultCode::PAM_SUCCESS == r && !ptr.is_null() {
- let typed_ptr: *const c_char = ptr as *const c_char;
- Some(CStr::from_ptr(typed_ptr).to_string_lossy().into_owned())
- } else {
- None
- };
- (r, t)
- };
- if PamResultCode::PAM_SUCCESS == res {
- Ok(item)
- } else {
- Err(res)
- }
- }
-}
-
-/// Provides functions that are invoked by the entrypoints generated by the
-/// [`pam_hooks!` macro](../macro.pam_hooks.html).
-///
-/// All of hooks are ignored by PAM dispatch by default given the default return value of `PAM_IGNORE`.
-/// Override any functions that you want to handle with your module. See `man pam(3)`.
-#[allow(unused_variables)]
-pub trait PamHooks {
- /// This function performs the task of establishing whether the user is permitted to gain access at
- /// this time. It should be understood that the user has previously been validated by an
- /// authentication module. This function checks for other things. Such things might be: the time of
- /// day or the date, the terminal line, remote hostname, etc. This function may also determine
- /// things like the expiration on passwords, and respond that the user change it before continuing.
- fn acct_mgmt(
- pamh: &PamHandle,
- args: Vec<&CStr>,
- flags: PamFlag,
- ) -> PamResultCode {
- PamResultCode::PAM_IGNORE
- }
-
- /// This function performs the task of authenticating the user.
- fn sm_authenticate(
- pamh: &PamHandle,
- args: Vec<&CStr>,
- flags: PamFlag,
- ) -> PamResultCode {
- PamResultCode::PAM_IGNORE
- }
-
- /// This function is used to (re-)set the authentication token of the user.
- ///
- /// The PAM library calls this function twice in succession. The first time with
- /// PAM_PRELIM_CHECK and then, if the module does not return PAM_TRY_AGAIN, subsequently with
- /// PAM_UPDATE_AUTHTOK. It is only on the second call that the authorization token is
- /// (possibly) changed.
- fn sm_chauthtok(
- pamh: &PamHandle,
- args: Vec<&CStr>,
- flags: PamFlag,
- ) -> PamResultCode {
- PamResultCode::PAM_IGNORE
- }
-
- /// This function is called to terminate a session.
- fn sm_close_session(
- pamh: &PamHandle,
- args: Vec<&CStr>,
- flags: PamFlag,
- ) -> PamResultCode {
- PamResultCode::PAM_IGNORE
- }
-
- /// This function is called to commence a session.
- fn sm_open_session(
- pamh: &PamHandle,
- args: Vec<&CStr>,
- flags: PamFlag,
- ) -> PamResultCode {
- PamResultCode::PAM_IGNORE
- }
-
- /// This function performs the task of altering the credentials of the user with respect to the
- /// corresponding authorization scheme. Generally, an authentication module may have access to more
- /// information about a user than their authentication token. This function is used to make such
- /// information available to the application. It should only be called after the user has been
- /// authenticated but before a session has been established.
- fn sm_setcred(
- pamh: &PamHandle,
- args: Vec<&CStr>,
- flags: PamFlag,
- ) -> PamResultCode {
- PamResultCode::PAM_IGNORE
- }
-}
+++ /dev/null
-[package]
-name = "param"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[dependencies]
-chelps = { workspace = true }
-dbg.workspace = true
-ntstatus_gen.workspace = true
-paste = "1.0.15"
-
-[build-dependencies]
-bindgen = "0.69.4"
+++ /dev/null
-use std::env;
-use std::path::PathBuf;
-
-fn main() {
- let bindings = bindgen::Builder::default()
- .blocklist_function("qgcvt")
- .blocklist_function("qgcvt_r")
- .blocklist_function("qfcvt")
- .blocklist_function("qfcvt_r")
- .blocklist_function("qecvt")
- .blocklist_function("qecvt_r")
- .blocklist_function("strtold")
- .clang_arg("-Dbool=int")
- .clang_arg("-Doffset_t=loff_t")
- .clang_arg("-I../../bin/default")
- .clang_arg("-I../../lib/talloc")
- .generate_comments(false)
- .clang_arg("-includestdint.h")
- .header("../../lib/param/param.h")
- .header("../../lib/param/loadparm.h")
- .header("../../source3/param/loadparm.h")
- .header("../../bin/default/lib/param/param_functions.h")
- .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
- .generate()
- .expect("Unable to generate bindings");
- println!("cargo:rerun-if-changed=../../lib/param/param.h");
- println!("cargo:rerun-if-changed=../../lib/param/loadparm.h");
- println!("cargo:rerun-if-changed=../../source3/param/loadparm.h");
- println!(
- "cargo:rerun-if-changed=../../bin/default/lib/param/param_functions.h"
- );
-
- let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
- bindings
- .write_to_file(out_path.join("bindings.rs"))
- .expect("Couldn't write bindings!");
-
- let mut src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
- src_dir.push("../../bin/default/source3");
- println!(
- "cargo:rustc-link-search=native={}",
- src_dir.to_str().unwrap()
- );
- println!("cargo:rustc-link-lib=smbconf");
-
- let mut src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
- src_dir.push("../../bin/default/lib/param");
- println!("cargo:rustc-link-lib=samba-hostconfig-private-samba");
- println!(
- "cargo:rustc-link-search=native={}",
- src_dir.to_str().unwrap()
- );
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Parameter loading functions
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-use chelps::{string_free, wrap_c_char, wrap_string};
-use dbg::{DBG_ERR, DBG_INFO, DBG_WARNING};
-use ntstatus_gen::NT_STATUS_UNSUCCESSFUL;
-use std::error::Error;
-use std::ffi::c_void;
-use std::ptr;
-use std::sync::{Arc, Mutex};
-
-mod ffi {
- #![allow(non_upper_case_globals)]
- #![allow(non_camel_case_types)]
- #![allow(non_snake_case)]
- #![allow(dead_code)]
- #![allow(clippy::upper_case_acronyms)]
- include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-}
-
-pub struct LoadParm {
- lp: Arc<Mutex<*mut ffi::loadparm_context>>,
-}
-
-macro_rules! lpcfg_str {
- ($var:ident) => {
- paste::item! {
- pub fn $var (&self) -> Result<Option<String>, Box<dyn Error + '_>> {
- let lp = self.lp.lock()?;
- let val = unsafe { ffi::[< lpcfg_ $var >] (*lp) } ;
- unsafe { Ok(wrap_c_char(val)) }
- }
- }
- };
-}
-
-macro_rules! lpcfg_i32 {
- ($var:ident) => {
- paste::item! {
- pub fn $var (&self) -> Result<i32, Box<dyn Error + '_>> {
- let lp = self.lp.lock()?;
- unsafe { Ok(ffi::[< lpcfg_ $var >] (*lp)) }
- }
- }
- };
-}
-
-macro_rules! lpcfg_bool {
- ($var:ident) => {
- paste::item! {
- pub fn $var (&self) -> Result<bool, Box<dyn Error + '_>> {
- let lp = self.lp.lock()?;
- unsafe { Ok(ffi::[< lpcfg_ $var >] (*lp) != 0) }
- }
- }
- };
-}
-
-impl LoadParm {
- pub fn new(configfile: Option<&str>) -> Result<Self, Box<dyn Error + '_>> {
- let lp = unsafe {
- match configfile {
- Some(configfile) => {
- let configfile_cstr = wrap_string(configfile);
- let lp = ffi::loadparm_init_global(0);
- if ffi::lpcfg_load(lp, configfile_cstr) != 1 {
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- string_free(configfile_cstr);
- lp
- }
- None => ffi::loadparm_init_global(1),
- }
- };
- Ok(LoadParm {
- lp: Arc::new(Mutex::new(lp)),
- })
- }
-
- pub fn private_path(
- &self,
- name: &str,
- ) -> Result<Option<String>, Box<dyn Error + '_>> {
- let lp = self.lp.lock()?;
- let path = unsafe {
- let name_cstr = wrap_string(name);
- let path =
- ffi::lpcfg_private_path(*lp as *mut c_void, *lp, name_cstr);
- string_free(name_cstr);
- path
- };
- unsafe { Ok(wrap_c_char(path)) }
- }
-
- pub fn logfile(&self) -> Result<Option<String>, Box<dyn Error + '_>> {
- let lp = self.lp.lock()?;
- let logfile = unsafe {
- let lp_sub = ffi::lpcfg_noop_substitution();
- ffi::lpcfg_logfile(*lp, lp_sub, *lp as *mut c_void)
- };
- unsafe { Ok(wrap_c_char(logfile)) }
- }
-
- pub fn idmap_range(
- &self,
- domain_name: &str,
- ) -> Result<(u32, u32), Box<dyn Error + '_>> {
- if let Ok(Some(backend)) = self.idmap_backend(domain_name) {
- if backend != "upn" {
- DBG_ERR!("Backend '{}' is not supported for Entra ID", backend);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- } else if domain_name != "*" {
- DBG_WARNING!(
- "No idmap backend configured for domain '{}'",
- domain_name
- );
- DBG_INFO!("Falling back to default idmap configuration");
- return self.idmap_range("*");
- } else {
- DBG_WARNING!("No idmap backend configured");
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- let lp = self.lp.lock()?;
- let parm_name = format!("idmap config {}", domain_name);
- let parm_opt = "range";
- let range = unsafe {
- let parm_name_cstr = wrap_string(&parm_name);
- let parm_opt_cstr = wrap_string(parm_opt);
- let parm_opt_value = ffi::lpcfg_parm_string(
- *lp,
- ptr::null_mut(),
- parm_name_cstr,
- parm_opt_cstr,
- );
- string_free(parm_name_cstr);
- string_free(parm_opt_cstr);
- wrap_c_char(parm_opt_value)
- }
- .ok_or(Box::new(NT_STATUS_UNSUCCESSFUL))?;
- let parts: Vec<&str> = range.split('-').collect();
- if parts.len() != 2 {
- DBG_ERR!("Failed to parse the idmap range: {}", range);
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- Ok((parts[0].parse::<u32>()?, parts[1].parse::<u32>()?))
- }
-
- pub fn idmap_backend(
- &self,
- domain_name: &str,
- ) -> Result<Option<String>, Box<dyn Error + '_>> {
- let lp = self.lp.lock()?;
- let parm_name = format!("idmap config {}", domain_name);
- let parm_opt = "backend";
- unsafe {
- let parm_name_cstr = wrap_string(&parm_name);
- let parm_opt_cstr = wrap_string(parm_opt);
- let parm_opt_value = ffi::lpcfg_parm_string(
- *lp,
- ptr::null_mut(),
- parm_name_cstr,
- parm_opt_cstr,
- );
- string_free(parm_name_cstr);
- string_free(parm_opt_cstr);
- Ok(wrap_c_char(parm_opt_value))
- }
- }
-
- lpcfg_str!(realm);
- lpcfg_str!(winbindd_socket_directory);
- lpcfg_i32!(winbind_request_timeout);
- lpcfg_bool!(himmelblaud_sfa_fallback);
- lpcfg_bool!(himmelblaud_hello_enabled);
- lpcfg_str!(cache_directory);
- lpcfg_str!(template_homedir);
- lpcfg_str!(template_shell);
- lpcfg_str!(himmelblaud_hsm_pin_path);
-}
-
-unsafe impl Send for LoadParm {}
-unsafe impl Sync for LoadParm {}
+++ /dev/null
-# Lightly enforce maximum 80 characters
-max_width = 80
+++ /dev/null
-[package]
-name = "sock"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[dependencies]
-chelps.workspace = true
-dbg.workspace = true
-libc.workspace = true
-libnss = "0.8.0"
-ntstatus_gen.workspace = true
-param.workspace = true
-serde = { version = "1.0.204", features = ["derive"] }
-serde_json = "1.0.120"
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Unix socket communication for the Himmelblau daemon
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-mod proto;
-pub use proto::*;
-
-use dbg::DBG_ERR;
-use ntstatus_gen::*;
-use param::LoadParm;
-use serde_json::{from_slice as json_from_slice, to_vec as json_to_vec};
-use std::error::Error;
-use std::io::{Read, Write};
-use std::os::unix::net::UnixStream;
-use std::path::{Path, PathBuf};
-use std::time::{Duration, SystemTime};
-
-pub struct ClientStream {
- stream: UnixStream,
-}
-
-impl ClientStream {
- pub fn new(path: &str) -> Result<Self, Box<dyn Error>> {
- Ok(ClientStream {
- stream: UnixStream::connect(path)
- .map_err(|e| {
- DBG_ERR!("Unix socket stream setup error while connecting to {}: {:?}",
- path, e
- );
- e
- })?,
- })
- }
-
- pub fn send(
- &mut self,
- req: &Request,
- timeout: u64,
- ) -> Result<Response, Box<dyn Error>> {
- // Set the timeout
- let timeout = Duration::from_secs(timeout);
- self.stream.set_read_timeout(Some(timeout))?;
- self.stream.set_write_timeout(Some(timeout))?;
-
- // Encode the request as bytes
- let req_bytes = json_to_vec(req)?;
-
- // Send the request
- self.stream.write_all(&req_bytes)?;
-
- // Now wait on the response
- let start = SystemTime::now();
- let mut read_started = false;
- let mut data = Vec::with_capacity(1024);
- let mut counter = 0;
-
- loop {
- let mut buffer = [0; 1024];
- let durr =
- SystemTime::now().duration_since(start).map_err(Box::new)?;
- if durr > timeout {
- DBG_ERR!("Socket timeout");
- break;
- }
- match self.stream.read(&mut buffer) {
- Ok(0) => {
- if read_started {
- break;
- } else {
- continue;
- }
- }
- Ok(count) => {
- data.extend_from_slice(&buffer);
- counter += count;
- if count == 1024 {
- read_started = true;
- continue;
- } else {
- break;
- }
- }
- Err(e) => {
- DBG_ERR!(
- "Stream read failure from {:?}: {:?}",
- &self.stream,
- e
- );
- return Err(Box::new(e));
- }
- }
- }
-
- data.truncate(counter);
-
- // Now decode the response
- let resp: Response = json_from_slice(data.as_slice())?;
- Ok(resp)
- }
-}
-
-pub fn stream_and_timeout(
- lp: &LoadParm,
-) -> Result<(ClientStream, u64), Box<NTSTATUS>> {
- // Get the socket path
- let sock_dir_str = lp
- .winbindd_socket_directory()
- .map_err(|_| Box::new(NT_STATUS_NOT_FOUND))?
- .ok_or(Box::new(NT_STATUS_NOT_FOUND))?;
- let sock_dir = Path::new(&sock_dir_str);
- let mut sock_path = PathBuf::from(sock_dir);
- sock_path.push("hb_pipe");
- let sock_path = sock_path.to_str().ok_or(Box::new(NT_STATUS_NOT_FOUND))?;
-
- // Open the socket
- let timeout: u64 = lp
- .winbind_request_timeout()
- .map_err(|_| Box::new(NT_STATUS_NOT_FOUND))?
- .try_into()
- .map_err(|_| Box::new(NT_STATUS_NOT_FOUND))?;
- let stream = ClientStream::new(sock_path)
- .map_err(|_| Box::new(NT_STATUS_PIPE_NOT_AVAILABLE))?;
- Ok((stream, timeout))
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Unix socket communication for the Himmelblau daemon
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use libc::uid_t;
-use libnss::group::Group as NssGroup;
-use libnss::passwd::Passwd as NssPasswd;
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct Passwd {
- pub name: String,
- pub passwd: String,
- pub uid: u32,
- pub gid: u32,
- pub gecos: String,
- pub dir: String,
- pub shell: String,
-}
-
-impl From<Passwd> for NssPasswd {
- fn from(val: Passwd) -> Self {
- NssPasswd {
- name: val.name,
- passwd: val.passwd,
- uid: val.uid,
- gid: val.gid,
- gecos: val.gecos,
- dir: val.dir,
- shell: val.shell,
- }
- }
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct Group {
- pub name: String,
- pub passwd: String,
- pub gid: u32,
- pub members: Vec<String>,
-}
-
-impl From<Group> for NssGroup {
- fn from(val: Group) -> Self {
- NssGroup {
- name: val.name,
- passwd: val.passwd,
- gid: val.gid,
- members: val.members,
- }
- }
-}
-
-#[derive(Serialize, Deserialize)]
-pub enum PamAuthRequest {
- Password { cred: String },
- MFACode { cred: String },
- MFAPoll { poll_attempt: u32 },
- SetupPin { pin: String },
- Pin { pin: String },
-}
-
-#[derive(Serialize, Deserialize)]
-pub enum Request {
- NssAccounts,
- NssAccountByUid(uid_t),
- NssAccountByName(String),
- NssGroups,
- NssGroupByGid(uid_t),
- NssGroupByName(String),
- PamAuthenticateInit(String),
- PamAuthenticateStep(PamAuthRequest),
- PamAccountAllowed(String),
- PamAccountBeginSession(String),
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub enum PamAuthResponse {
- Unknown,
- Success,
- Denied,
- Password,
- MFACode { msg: String },
- MFAPoll { msg: String, polling_interval: u32 },
- MFAPollWait,
- SetupPin { msg: String },
- Pin,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub enum Response {
- NssAccounts(Vec<Passwd>),
- NssAccount(Option<Passwd>),
- NssGroups(Vec<Group>),
- NssGroup(Option<Group>),
- PamStatus(Option<bool>),
- PamAuthStepResponse(PamAuthResponse),
- Success,
- Error,
-}
+++ /dev/null
-[package]
-name = "talloc"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[build-dependencies]
-bindgen = "0.69.4"
-config.workspace = true
-
-[dependencies]
-chelps.workspace = true
+++ /dev/null
-use std::env;
-use std::path::PathBuf;
-
-fn main() {
- let bindings = bindgen::Builder::default()
- .blocklist_function("qgcvt")
- .blocklist_function("qgcvt_r")
- .blocklist_function("qfcvt")
- .blocklist_function("qfcvt_r")
- .blocklist_function("qecvt")
- .blocklist_function("qecvt_r")
- .blocklist_function("strtold")
- .clang_arg("-Dbool=int")
- .generate_comments(false)
- .clang_arg("-I../../lib/talloc")
- .header("../../lib/util/talloc_stack.h")
- .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
- .generate()
- .expect("Unable to generate bindings");
- println!("cargo:rerun-if-changed=../../lib/util/talloc_stack.h");
-
- let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
- bindings
- .write_to_file(out_path.join("bindings.rs"))
- .expect("Couldn't write bindings!");
-
- let mut src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
- src_dir.push("../../bin/default/lib/talloc");
- if config::USING_SYSTEM_TALLOC == 1 {
- println!("cargo:rustc-link-lib=talloc");
- } else {
- println!("cargo:rustc-link-lib=talloc-private-samba");
- }
- println!(
- "cargo:rustc-link-search=native={}",
- src_dir.to_str().unwrap()
- );
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- Talloc stackframe functions
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-pub mod ffi {
- #![allow(non_upper_case_globals)]
- #![allow(non_camel_case_types)]
- #![allow(non_snake_case)]
- #![allow(dead_code)]
- #![allow(clippy::upper_case_acronyms)]
- include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-}
-
-#[macro_export]
-macro_rules! talloc_stackframe {
- () => {{
- let function = chelps::function!();
- let function_cstr = chelps::wrap_string(&function);
- unsafe {
- let ret = $crate::ffi::_talloc_stackframe(function_cstr);
- chelps::string_free(function_cstr);
- ret
- }
- }};
-}
-
-#[macro_export]
-macro_rules! TALLOC_FREE {
- ($ctx:ident) => {{
- if !$ctx.is_null() {
- let function = chelps::function!();
- let function_cstr = chelps::wrap_string(&function);
- unsafe {
- $crate::ffi::_talloc_free($ctx, function_cstr);
- chelps::string_free(function_cstr);
- }
- }
- }};
-}
+++ /dev/null
-[package]
-name = "tdb"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[dependencies]
-chelps = { workspace = true }
-dbg.workspace = true
-libc.workspace = true
-ntstatus_gen.workspace = true
-
-[build-dependencies]
-bindgen = "0.69.4"
-config.workspace = true
+++ /dev/null
-use std::env;
-use std::path::PathBuf;
-
-fn main() {
- let bindings = bindgen::Builder::default()
- .blocklist_function("qgcvt")
- .blocklist_function("qgcvt_r")
- .blocklist_function("qfcvt")
- .blocklist_function("qfcvt_r")
- .blocklist_function("qecvt")
- .blocklist_function("qecvt_r")
- .blocklist_function("strtold")
- .clang_arg("-includesys/stat.h")
- .header("../../lib/tdb/include/tdb.h")
- .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
- .generate()
- .expect("Unable to generate bindings");
-
- let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
- bindings
- .write_to_file(out_path.join("bindings.rs"))
- .expect("Couldn't write bindings!");
-
- let mut src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
- src_dir.push("../../bin/default/lib/tdb");
- if config::USING_SYSTEM_TDB == 1 {
- println!("cargo:rustc-link-lib=tdb");
- } else {
- println!("cargo:rustc-link-lib=tdb-private-samba");
- }
- println!(
- "cargo:rustc-link-search=native={}",
- src_dir.to_str().unwrap()
- );
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
-
- trivial database library FFI
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-use chelps::wrap_string;
-use dbg::DBG_ERR;
-use libc::free;
-use ntstatus_gen::NT_STATUS_UNSUCCESSFUL;
-use std::error::Error;
-use std::ffi::c_void;
-use std::fmt;
-use std::path::PathBuf;
-use std::sync::{Arc, Mutex};
-
-mod ffi {
- #![allow(non_upper_case_globals)]
- #![allow(non_camel_case_types)]
- #![allow(non_snake_case)]
- #![allow(dead_code)]
- #![allow(clippy::upper_case_acronyms)]
- include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-}
-
-pub const TDB_SUCCESS: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_SUCCESS;
-pub const TDB_ERR_CORRUPT: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_CORRUPT;
-pub const TDB_ERR_IO: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_IO;
-pub const TDB_ERR_LOCK: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_LOCK;
-pub const TDB_ERR_OOM: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_OOM;
-pub const TDB_ERR_EXISTS: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_EXISTS;
-pub const TDB_ERR_NOLOCK: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_NOLOCK;
-pub const TDB_ERR_LOCK_TIMEOUT: ffi::TDB_ERROR =
- ffi::TDB_ERROR_TDB_ERR_LOCK_TIMEOUT;
-pub const TDB_ERR_NOEXIST: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_NOEXIST;
-pub const TDB_ERR_EINVAL: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_EINVAL;
-pub const TDB_ERR_RDONLY: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_RDONLY;
-pub const TDB_ERR_NESTING: ffi::TDB_ERROR = ffi::TDB_ERROR_TDB_ERR_NESTING;
-
-#[allow(non_camel_case_types)]
-#[derive(PartialEq, Eq)]
-pub struct TDB_ERROR(pub u32);
-
-impl TDB_ERROR {
- fn description(&self) -> &str {
- match self.0 {
- ffi::TDB_ERROR_TDB_SUCCESS => "TDB_SUCCESS",
- ffi::TDB_ERROR_TDB_ERR_CORRUPT => "TDB_ERR_CORRUPT",
- ffi::TDB_ERROR_TDB_ERR_IO => "TDB_ERR_IO",
- ffi::TDB_ERROR_TDB_ERR_LOCK => "TDB_ERR_LOCK",
- ffi::TDB_ERROR_TDB_ERR_OOM => "TDB_ERR_OOM",
- ffi::TDB_ERROR_TDB_ERR_EXISTS => "TDB_ERR_EXISTS",
- ffi::TDB_ERROR_TDB_ERR_NOLOCK => "TDB_ERR_NOLOCK",
- ffi::TDB_ERROR_TDB_ERR_LOCK_TIMEOUT => "TDB_ERR_LOCK_TIMEOUT",
- ffi::TDB_ERROR_TDB_ERR_NOEXIST => "TDB_ERR_NOEXIST",
- ffi::TDB_ERROR_TDB_ERR_EINVAL => "TDB_ERR_EINVAL",
- ffi::TDB_ERROR_TDB_ERR_RDONLY => "TDB_ERR_RDONLY",
- ffi::TDB_ERROR_TDB_ERR_NESTING => "TDB_ERR_NESTING",
- _ => "Unknown TDB_ERROR error code",
- }
- }
-}
-
-macro_rules! lock_check {
- ($res:expr) => {{
- $res.map_err(|e| {
- DBG_ERR!("Failed to obtain tdb lock: {:?}", e);
- Box::new(TDB_ERROR(TDB_ERR_NOLOCK))
- })?
- }};
-}
-
-impl fmt::Display for TDB_ERROR {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "TDB_ERROR({:#x}): {}", self.0, self.description())
- }
-}
-
-impl fmt::Debug for TDB_ERROR {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "TDB_ERROR({:#x})", self.0)
- }
-}
-
-impl std::error::Error for TDB_ERROR {}
-
-#[derive(Clone)]
-pub struct Tdb {
- tdb: Arc<Mutex<*mut ffi::tdb_context>>,
-}
-
-impl Tdb {
- pub fn open(
- name: &str,
- hash_size: Option<i32>,
- tdb_flags: Option<i32>,
- open_flags: Option<i32>,
- mode: Option<u32>,
- ) -> Result<Self, Box<dyn Error + '_>> {
- let path = PathBuf::from(name);
- let tdb = unsafe {
- ffi::tdb_open(
- wrap_string(name),
- hash_size.unwrap_or(0),
- tdb_flags.unwrap_or(ffi::TDB_DEFAULT as i32),
- match open_flags {
- Some(open_flags) => open_flags,
- None => {
- if path.exists() {
- libc::O_RDWR
- } else {
- libc::O_RDWR | libc::O_CREAT
- }
- }
- },
- mode.unwrap_or(0o600),
- )
- };
- if tdb.is_null() {
- return Err(Box::new(NT_STATUS_UNSUCCESSFUL));
- }
- Ok(Tdb {
- tdb: Arc::new(Mutex::new(tdb)),
- })
- }
-
- pub fn transaction_start(&mut self) -> Result<bool, Box<dyn Error + '_>> {
- let tdb = self.tdb.lock()?;
- unsafe { Ok(ffi::tdb_transaction_start(*tdb) == 0) }
- }
-
- pub fn transaction_commit(&mut self) -> Result<bool, Box<dyn Error + '_>> {
- let tdb = self.tdb.lock()?;
- unsafe { Ok(ffi::tdb_transaction_commit(*tdb) == 0) }
- }
-
- pub fn transaction_cancel(&mut self) -> Result<bool, Box<dyn Error + '_>> {
- let tdb = self.tdb.lock()?;
- unsafe { Ok(ffi::tdb_transaction_cancel(*tdb) == 0) }
- }
-
- pub fn exists(&self, key: &str) -> Result<bool, Box<dyn Error + '_>> {
- let tdb = lock_check!(self.tdb.lock());
- let key = ffi::TDB_DATA {
- dptr: wrap_string(key) as *mut u8,
- dsize: key.len(),
- };
- unsafe { Ok(ffi::tdb_exists(*tdb, key) == 1) }
- }
-
- pub fn fetch(&self, key: &str) -> Result<String, Box<dyn Error + '_>> {
- let tdb = self.tdb.lock()?;
- let key = ffi::TDB_DATA {
- dptr: wrap_string(key) as *mut u8,
- dsize: key.len(),
- };
- let res = unsafe { ffi::tdb_fetch(*tdb, key) };
- if res.dptr.is_null() {
- let err = unsafe { ffi::tdb_error(*tdb) };
- return Err(Box::new(TDB_ERROR(err)));
- }
- Ok(unsafe {
- std::str::from_utf8_unchecked(std::slice::from_raw_parts(
- res.dptr, res.dsize,
- ))
- }
- .to_string())
- }
-
- pub fn delete(&mut self, key: &str) -> Result<bool, Box<dyn Error + '_>> {
- let tdb = self.tdb.lock()?;
- let key = ffi::TDB_DATA {
- dptr: wrap_string(key) as *mut u8,
- dsize: key.len(),
- };
- unsafe { Ok(ffi::tdb_delete(*tdb, key) == 0) }
- }
-
- pub fn store(
- &mut self,
- key: &str,
- dbuf: &[u8],
- flag: Option<u32>,
- ) -> Result<bool, Box<dyn Error + '_>> {
- let tdb = self.tdb.lock()?;
- let flag = match flag {
- Some(flag) => flag,
- None => ffi::TDB_REPLACE,
- };
- let key = ffi::TDB_DATA {
- dptr: wrap_string(key) as *mut u8,
- dsize: key.len(),
- };
- let dbuf = ffi::TDB_DATA {
- dptr: dbuf.as_ptr() as *mut u8,
- dsize: dbuf.len(),
- };
- unsafe { Ok(ffi::tdb_store(*tdb, key, dbuf, flag as i32) == 0) }
- }
-
- pub fn keys(&self) -> Result<Vec<String>, Box<dyn Error + '_>> {
- let mut res = Vec::new();
- let tdb = self.tdb.lock()?;
- let mut key = unsafe { ffi::tdb_firstkey(*tdb) };
- if key.dptr.is_null() {
- return Ok(res);
- }
- let rkey = unsafe {
- std::str::from_utf8_unchecked(std::slice::from_raw_parts(
- key.dptr, key.dsize,
- ))
- }
- .to_string();
- res.push(rkey);
-
- loop {
- let next = unsafe { ffi::tdb_nextkey(*tdb, key) };
- unsafe { free(key.dptr as *mut c_void) };
- if next.dptr.is_null() {
- break;
- } else {
- let rkey = unsafe {
- std::str::from_utf8_unchecked(std::slice::from_raw_parts(
- next.dptr, next.dsize,
- ))
- }
- .to_string();
- res.push(rkey);
- }
- key = next;
- }
-
- Ok(res)
- }
-}
-
-impl Drop for Tdb {
- fn drop(&mut self) {
- let tdb = self.tdb.lock().unwrap();
- unsafe { ffi::tdb_close(*tdb) };
- }
-}
-
-unsafe impl Send for Tdb {}
-unsafe impl Sync for Tdb {}
+++ /dev/null
-[package]
-name = "version"
-edition.workspace = true
-license.workspace = true
-homepage.workspace = true
-version.workspace = true
-
-[lib]
-name = "version"
-path = "src/lib.rs"
-
-[dependencies]
-chelps.workspace = true
-libc.workspace = true
-
-[build-dependencies]
-cc = "1.0.97"
-bindgen = "0.69.4"
+++ /dev/null
-use std::env;
-use std::path::{Path, PathBuf};
-
-fn main() {
- cc::Build::new()
- .file("../../source3/lib/version.c")
- .include(Path::new("../../bin/default"))
- .include(Path::new("./include")) // for the empty includes.h
- .warnings(false)
- .compile("version");
-
- let bindings = bindgen::Builder::default()
- .blocklist_function("qgcvt")
- .blocklist_function("qgcvt_r")
- .blocklist_function("qfcvt")
- .blocklist_function("qfcvt_r")
- .blocklist_function("qecvt")
- .blocklist_function("qecvt_r")
- .blocklist_function("strtold")
- .header("../../bin/default/version.h")
- .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
- .generate()
- .expect("Unable to generate bindings");
-
- let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
- bindings
- .write_to_file(out_path.join("bindings.rs"))
- .expect("Couldn't write bindings!");
-}
+++ /dev/null
-/*
- Unix SMB/CIFS implementation.
- Samba Version functions
-
- Copyright (C) David Mulder 2024
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-use std::str;
-
-include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
-
-pub fn samba_version_string() -> Option<String> {
- let null_trimmed_vers =
- &SAMBA_VERSION_STRING[..SAMBA_VERSION_STRING.len() - 1];
- match str::from_utf8(null_trimmed_vers) {
- Ok(vers) => Some(vers.to_string()),
- Err(_) => None,
- }
-}
-
-pub fn samba_copyright_string() -> Option<String> {
- let null_trimmed_copy =
- &SAMBA_COPYRIGHT_STRING[..SAMBA_COPYRIGHT_STRING.len() - 1];
- match str::from_utf8(null_trimmed_copy) {
- Ok(copy) => Some(copy.to_string()),
- Err(_) => None,
- }
-}
bld.vendor_sources()
-if conf.env.enable_himmelblau:
- bld.SAMBA_RUST_BINARY('himmelblaud', 'himmelblaud',
- ['param',
- 'chelps',
- 'dbg',
- 'ntstatus_gen',
- 'sock',
- 'tdb',
- 'version',
- 'idmap'])
- bld.SAMBA_RUST_LIBRARY('nss', 'libnss_himmelblau.so', ['sock',
- 'ntstatus_gen',
- 'param',
- 'version'])
- bld.SAMBA_RUST_LIBRARY('pam', 'libpam_himmelblau.so', ['sock',
- 'chelps',
- 'param',
- 'version'])
+# There are no Rust integrations at the moment. If there were, they would be built here.
ctdb_configure_params = " --enable-developer ${PREFIX}"
samba_configure_params = " ${ENABLE_COVERAGE} ${PREFIX} --with-profiling-data --with-prometheus-exporter"
-# We cannot configure himmelblau on old systems missing openssl 3, with glibc
-# older than version 2.32, or when cargo isn't available.
-himmelblau_configure_params = ''
rust_configure_param = ''
glibc_vers = float('.'.join(get_libc_version().split('.')[:2]))
cargo = shutil.which('cargo')
if glibc_vers >= 2.32 and cargo != None:
rust_configure_param = ' --enable-rust'
-if ssl.OPENSSL_VERSION_INFO[0] >= 3 and rust_configure_param:
- himmelblau_configure_params = rust_configure_param + ' --with-himmelblau'
samba_libs_envvars = "PYTHONPATH=${PYTHON_PREFIX}:$PYTHONPATH"
samba_libs_envvars += " PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${PREFIX_DIR}/lib/pkgconfig"
"samba-o3": {
"sequence": [
("random-sleep", random_sleep(300, 900)),
- ("configure", "ADDITIONAL_CFLAGS='" + samba_o3_cflags + "' ./configure.developer --abi-check-disable" + himmelblau_configure_params + samba_configure_params),
+ ("configure", "ADDITIONAL_CFLAGS='" + samba_o3_cflags + "' ./configure.developer --abi-check-disable" + samba_configure_params),
("make", "make -j"),
("test", make_test(cmd='make test', TESTS="--exclude=selftest/slow-none", include_envs=["none"])),
("quicktest", make_test(cmd='make quicktest', include_envs=["ad_dc", "ad_dc_smb1", "ad_dc_smb1_done"])),
Globals.acl_claims_evaluation = ACL_CLAIMS_EVALUATION_AD_DC_ONLY;
- /* Set the default Himmelblaud globals */
- lpcfg_string_set(Globals.ctx,
- &Globals.himmelblaud_hsm_pin_path,
- get_dyn_HIMMELBLAUD_HSM_PIN_PATH());
- Globals.himmelblaud_hello_enabled = false;
- Globals.himmelblaud_sfa_fallback = false;
-
Globals.server_smb_encryption_over_quic = true;
Globals.client_smb_encryption_over_quic = true;
dest='SAMBA_VERSION_VENDOR_SUFFIX',
default=None)
- opt.add_option('--with-himmelblau', default=False,
- help=('Build with Azure Entra ID support.'),
- action='store_true', dest='enable_himmelblau')
-
opt.samba_add_onoff_option('systemd-userdb',
help=("Build winbind with support for systemd "
"User/Group Record Lookup API via "
conf.env.debug = Options.options.debug
conf.env.developer = Options.options.developer
- conf.env.enable_himmelblau = Options.options.enable_himmelblau
- if Options.options.enable_himmelblau:
- if not conf.env.enable_rust:
- conf.fatal('--with-himmelblau cannot be specified without '
- '--enable-rust')
- if ssl.OPENSSL_VERSION_INFO[0] < 3:
- conf.fatal('--with-himmelblau cannot be specified with '
- '%s' % ssl.OPENSSL_VERSION)
#
# FreeBSD is broken. It doesn't include 'extern char **environ'