diff --git a/Cargo.lock b/Cargo.lock index 2bc9f6801..907412621 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,7 +12,7 @@ dependencies = [ "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -91,7 +91,7 @@ dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -129,7 +129,7 @@ name = "aho-corasick" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -165,8 +165,8 @@ name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -182,12 +182,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.43" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -197,7 +197,7 @@ version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -274,7 +274,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -283,7 +283,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "brotli-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -292,7 +292,7 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -426,7 +426,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "publicsuffix 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -437,7 +437,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -472,7 +472,7 @@ dependencies = [ "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "tinytemplate 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -496,11 +496,12 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -518,15 +519,16 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -535,7 +537,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -554,10 +556,10 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -568,7 +570,7 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bstr 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", @@ -576,10 +578,10 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.6" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -598,10 +600,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -611,7 +613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -621,9 +623,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "derive_builder_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -632,9 +634,9 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -660,7 +662,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -757,15 +759,15 @@ name = "error-chain" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "error-chain" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -773,7 +775,7 @@ name = "failure" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -782,9 +784,9 @@ name = "failure_derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -819,7 +821,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -924,9 +926,9 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -950,7 +952,7 @@ dependencies = [ "futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -972,7 +974,7 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1013,7 +1015,7 @@ dependencies = [ [[package]] name = "half" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1031,10 +1033,10 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1042,7 +1044,7 @@ name = "hostname" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1171,7 +1173,7 @@ name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1236,7 +1238,7 @@ dependencies = [ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_cbor 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "sshkeys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1255,10 +1257,10 @@ dependencies = [ "kanidm 0.1.1", "kanidm_proto 0.1.1", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1270,7 +1272,7 @@ dependencies = [ "actix 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "zxcvbn 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1286,7 +1288,7 @@ dependencies = [ "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rpassword 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "shellexpand 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "zxcvbn 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1303,11 +1305,12 @@ dependencies = [ "kanidm 0.1.1", "kanidm_client 0.1.1", "kanidm_proto 0.1.1", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2_sqlite 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rpassword 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_cbor 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1315,6 +1318,7 @@ dependencies = [ "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1343,7 +1347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1352,8 +1356,8 @@ version = "0.2.0" source = "git+https://github.com/csnewman/libnss-rs.git?rev=eab2d93d2438652773699b0807d558ce75b1e748#eab2d93d2438652773699b0807d558ce75b1e748" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1384,7 +1388,7 @@ name = "lock_api" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1423,11 +1427,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memchr" -version = "2.3.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "memoffset" @@ -1462,7 +1463,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1483,7 +1484,7 @@ dependencies = [ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1508,7 +1509,7 @@ version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1538,7 +1539,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1555,7 +1556,7 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1569,7 +1570,7 @@ name = "nom" version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1579,9 +1580,9 @@ version = "0.1.1" dependencies = [ "kanidm_unix_int 0.1.1", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "libnss 0.2.0 (git+https://github.com/csnewman/libnss-rs.git?rev=eab2d93d2438652773699b0807d558ce75b1e748)", - "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1659,8 +1660,8 @@ name = "num_cpus" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1677,7 +1678,7 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1693,7 +1694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1710,7 +1711,10 @@ dependencies = [ name = "pam_kanidm" version = "0.1.1" dependencies = [ + "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "kanidm_unix_int 0.1.1", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1755,7 +1759,7 @@ name = "parking_lot_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1767,7 +1771,7 @@ name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1781,7 +1785,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1795,7 +1799,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1803,22 +1807,22 @@ dependencies = [ [[package]] name = "paste" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "paste-impl 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "paste-impl" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1844,9 +1848,9 @@ name = "pin-project-internal" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1885,9 +1889,9 @@ name = "proc-macro-hack" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1905,7 +1909,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1916,7 +1920,7 @@ name = "publicsuffix" version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1941,7 +1945,7 @@ name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1970,7 +1974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1981,7 +1985,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1999,7 +2003,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2073,7 +2077,7 @@ name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2085,7 +2089,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2113,7 +2117,7 @@ name = "rayon" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2123,9 +2127,9 @@ name = "rayon-core" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2159,7 +2163,7 @@ version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2187,7 +2191,7 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2210,7 +2214,7 @@ dependencies = [ "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2238,7 +2242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2248,7 +2252,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2263,7 +2267,7 @@ dependencies = [ "fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2275,7 +2279,7 @@ dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2338,7 +2342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "scopeguard" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2348,7 +2352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2387,7 +2391,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "half 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "half 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2396,14 +2400,14 @@ name = "serde_derive" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2464,7 +2468,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2491,7 +2495,7 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2561,10 +2565,10 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2574,9 +2578,9 @@ name = "synstructure" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2586,7 +2590,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2606,7 +2610,7 @@ name = "termios" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2630,7 +2634,7 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2641,7 +2645,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2677,8 +2681,8 @@ dependencies = [ "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2714,7 +2718,7 @@ name = "tokio-executor" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2743,9 +2747,9 @@ name = "tokio-macros" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2763,7 +2767,7 @@ name = "tokio-reactor" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2782,7 +2786,7 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2819,9 +2823,9 @@ name = "tokio-threadpool" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2835,7 +2839,7 @@ name = "tokio-timer" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2872,7 +2876,7 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3082,9 +3086,9 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3142,7 +3146,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-macro 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3154,9 +3158,9 @@ dependencies = [ "bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3185,9 +3189,9 @@ name = "wasm-bindgen-macro-support" version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3205,9 +3209,9 @@ dependencies = [ "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-backend 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3338,7 +3342,7 @@ dependencies = [ "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum backtrace 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)" = "7f80256bc78f67e7df7e36d77366f636ed976895d91fe2ab9efa3973e8fe8c4f" +"checksum backtrace 0.3.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536" "checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" @@ -3375,15 +3379,15 @@ dependencies = [ "checksum criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc755679c12bda8e5523a71e4d654b6bf2e14bd838dfc48cde6559a05caf7d1" "checksum criterion-plot 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a01e15e0ea58e8234f96146b1f91fa9d0e4dd7a38da93ff7a75d42c0b9d3a545" "checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" -"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" +"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" "checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9" -"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" +"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" "checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" "checksum csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279" -"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" +"checksum csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" "checksum darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" "checksum darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" "checksum darling_macro 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" @@ -3403,7 +3407,7 @@ dependencies = [ "checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" "checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" -"checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" +"checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" "checksum error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6930e04918388a9a2e41d518c25cf679ccafe26733fb4127dbf21993f2575d46" "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" @@ -3433,10 +3437,10 @@ dependencies = [ "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" "checksum h2 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9433d71e471c1736fd5a61b671fc0b148d7a2992f666c958d03cd8feb3b88d1" -"checksum half 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ff54597ea139063f4225f1ec47011b03c9de4a486957ff3fc506881dac951d0" +"checksum half 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20d6a47d6e4b8559729f58287efa8e6f767e603c068fea7a5e4d9f1cebe2bebb" "checksum hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" +"checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" "checksum http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" "checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" @@ -3459,7 +3463,7 @@ dependencies = [ "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" "checksum libnss 0.2.0 (git+https://github.com/csnewman/libnss-rs.git?rev=eab2d93d2438652773699b0807d558ce75b1e748)" = "" "checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" @@ -3470,7 +3474,7 @@ dependencies = [ "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" +"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" "checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" @@ -3507,8 +3511,8 @@ dependencies = [ "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" -"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" -"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" +"checksum paste 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "63e1afe738d71b1ebab5f1207c055054015427dbfc7bbe9ee1266894156ec046" +"checksum paste-impl 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc4a7f6f743211c5aab239640a65091535d97d43d92a52bca435a640892bb" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" @@ -3521,7 +3525,7 @@ dependencies = [ "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" "checksum publicsuffix 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" "checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" @@ -3552,7 +3556,7 @@ dependencies = [ "checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" "checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum reqwest 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0e798e19e258bf6c30a304622e3e9ac820e483b06a1857a026e1f109b113fe4" +"checksum reqwest 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bae3fc32eacd4a5200c6b34bd6c057b07fb64f5a1e55bb67d624cc1393354621" "checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" "checksum ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a" "checksum rpassword 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "177b45ffb3ad20daed91c936e4670f008a8bc4af771aa9103d73e784da733879" @@ -3567,7 +3571,7 @@ dependencies = [ "checksum schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" "checksum scheduled-thread-pool 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f5de7bc31f28f8e6c28df5e1bf3d10610f5fdc14cc95f272853512c70a2bd779" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" "checksum security-framework 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" "checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" @@ -3575,7 +3579,7 @@ dependencies = [ "checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" "checksum serde_cbor 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7081ed758ec726a6ed8ee7e92f5d3f6e6f8c3901b1f972e3a4a2f2599fad14f" "checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" -"checksum serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "15913895b61e0be854afd32fd4163fcd2a3df34142cf2cb961b310ce694cbf90" +"checksum serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" "checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" @@ -3594,7 +3598,7 @@ dependencies = [ "checksum structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" "checksum structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" diff --git a/Cargo.toml b/Cargo.toml index cebd878fb..d40853040 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,11 @@ members = [ "kanidm_client", "kanidm_tools", "kanidm_unix_int", - "kanidm_unix_int/nss_kanidm", - "kanidm_unix_int/pam_kanidm" + "kanidm_unix_int/nss_kanidm", + "kanidm_unix_int/pam_kanidm", +] + +exclude = [ + "kanidm_unix_int/pam_tester" ] diff --git a/Makefile b/Makefile index 34964ab1a..b5674b596 100644 --- a/Makefile +++ b/Makefile @@ -9,3 +9,5 @@ vendor-prep: cargo vendor tar -czf vendor.tar.gz vendor +doc-local: + cargo doc --document-private-items diff --git a/kanidm_book/src/pam_and_nsswitch.md b/kanidm_book/src/pam_and_nsswitch.md index 929760db5..6a3f2b2d4 100644 --- a/kanidm_book/src/pam_and_nsswitch.md +++ b/kanidm_book/src/pam_and_nsswitch.md @@ -18,8 +18,14 @@ You can check the daemon is running on your Linux system with # systemctl status kanidm_unixd -This daemon uses configuration from /etc/kanidm/config. This is the covered in -client_tools. +This daemon uses connection configuration from /etc/kanidm/config. This is the covered in +client_tools. You can also configure some details of the unixd daemon in /etc/kanidm/unixd. + + pam_allowed_login_groups = ["posix_group"] + +The `pam_allowed_login_groups` defines a set of posix groups where membership of any of these +groups will be allowed to login via pam. All posix users and groups can be resolved by nss +regardless of pam login status. You can then check the communication status of the daemon as any user account. @@ -39,8 +45,8 @@ For more, see troubleshooting. When the daemon is running you can add the nsswitch libraries to /etc/nsswitch.conf - passwd: kanidm compat - group: kanidm compat + passwd: compat kanidm + group: compat kanidm You can then test that a posix extended user is able to be resolved with: @@ -61,10 +67,77 @@ You can also do the same for groups. > shell open while making changes (ie root), or have access to single-user mode > at the machines console. -TBD +PAM (Pluggable Authentication Modules) is how a unix like system allows users to authenticate +and be authorised to start interactive sessions. This is configured through a stack of modules +that are executed in order to evaluate the request. This is done through a series of steps +where each module may request or reused authentication token information. + +### Before you start + +You *should* backup your /etc/pam.d directory from it's original state as you *may* change the +pam config in a way that will cause you to be unable to authenticate to your machine. + + cp -a /etc/pam.d /root/pam.d.backup + +### SUSE + +To configure PAM on suse you must module four files: + + /etc/pam.d/common-account-pc + /etc/pam.d/common-auth-pc + /etc/pam.d/common-password-pc + /etc/pam.d/common-session-pc + +Each of these controls one of the four stages of pam. The content should look like: + + # /etc/pam.d/common-account-pc + account [default=1 ignore=ignore success=ok] pam_localuser.so + account required pam_unix.so + account required pam_kanidm.so ignore_unknown_user + + # /etc/pam.d/common-auth-pc + auth required pam_env.so + auth [default=1 ignore=ignore success=ok] pam_localuser.so + auth sufficient pam_unix.so nullok try_first_pass + auth requisite pam_succeed_if.so uid >= 1000 quiet_success + auth sufficient pam_kanidm.so debug ignore_unknown_user + auth required pam_deny.so + + # /etc/pam.d/common-password-pc + password requisite pam_cracklib.so + password [default=1 ignore=ignore success=ok] pam_localuser.so + password required pam_unix.so use_authtok nullok shadow try_first_pass + password required pam_kanidm.so + + # /etc/pam.d/common-session-pc + session optional pam_systemd.so + session required pam_limits.so + session required pam_mkhomedir.so skel=/etc/skel/ umask=0022 + session optional pam_unix.so try_first_pass + session optional pam_kanidm.so + session optional pam_umask.so + session optional pam_env.so + ## Troubleshooting +### Increase logging + +For the unixd daemon, you can increase the logging with: + + systemctl edit kanidm-unixd.service + +And add the lines: + + [Service] + Environment="RUST_LOG=kanidm=debug" + +Then restart the kanidm-unixd.service. + +To debug the pam module interactions add `debug` to the module arguments such as: + + auth sufficient pam_kanidm.so debug + ### Check the socket permissions Check that the /var/run/kanidm.sock is 777, and that non-root readers can see it with diff --git a/kanidm_client/src/asynchronous.rs b/kanidm_client/src/asynchronous.rs index a7bbdec44..d47048b3f 100644 --- a/kanidm_client/src/asynchronous.rs +++ b/kanidm_client/src/asynchronous.rs @@ -44,6 +44,37 @@ impl KanidmAsyncClient { Ok(r) } + async fn perform_put_request( + &self, + dest: &str, + request: R, + ) -> Result { + let dest = [self.addr.as_str(), dest].concat(); + debug!("{:?}", dest); + // format doesn't work in async ?! + // let dest = format!("{}{}", self.addr, dest); + + let req_string = serde_json::to_string(&request).unwrap(); + + let response = self + .client + .put(dest.as_str()) + .body(req_string) + .send() + .await + .map_err(ClientError::Transport)?; + + match response.status() { + reqwest::StatusCode::OK => {} + unexpect => return Err(ClientError::Http(unexpect, response.json().await.ok())), + } + + // TODO: What about errors + let r: T = response.json().await.unwrap(); + + Ok(r) + } + async fn perform_get_request(&self, dest: &str) -> Result { let dest = [self.addr.as_str(), dest].concat(); debug!("{:?}", dest); @@ -187,4 +218,42 @@ impl KanidmAsyncClient { self.perform_delete_request(["/v1/group/", id].concat().as_str()) .await } + + pub async fn idm_account_unix_cred_put(&self, id: &str, cred: &str) -> Result<(), ClientError> { + let req = SingleStringRequest { + value: cred.to_string(), + }; + self.perform_put_request( + ["/v1/account/", id, "/_unix/_credential"].concat().as_str(), + req, + ) + .await + } + + pub async fn idm_account_unix_cred_delete(&self, id: &str) -> Result<(), ClientError> { + self.perform_delete_request(["/v1/account/", id, "/_unix/_credential"].concat().as_str()) + .await + } + + pub async fn idm_account_unix_cred_verify( + &self, + id: &str, + cred: &str, + ) -> Result, ClientError> { + let req = SingleStringRequest { + value: cred.to_string(), + }; + self.perform_post_request(["/v1/account/", id, "/_unix/_auth"].concat().as_str(), req) + .await + } + + pub async fn idm_group_add_members( + &self, + id: &str, + members: Vec<&str>, + ) -> Result<(), ClientError> { + let m: Vec<_> = members.iter().map(|v| (*v).to_string()).collect(); + self.perform_post_request(["/v1/group/", id, "/_attr/member"].concat().as_str(), m) + .await + } } diff --git a/kanidm_client/src/lib.rs b/kanidm_client/src/lib.rs index acbf5da5e..2cf3915a7 100644 --- a/kanidm_client/src/lib.rs +++ b/kanidm_client/src/lib.rs @@ -205,7 +205,9 @@ impl KanidmClientBuilder { }; let client_builder = match &self.connect_timeout { - Some(secs) => client_builder.connect_timeout(Duration::from_secs(*secs)), + Some(secs) => client_builder + .connect_timeout(Duration::from_secs(*secs)) + .timeout(Duration::from_secs(*secs)), None => client_builder, }; @@ -239,7 +241,9 @@ impl KanidmClientBuilder { }; let client_builder = match &self.connect_timeout { - Some(secs) => client_builder.connect_timeout(Duration::from_secs(*secs)), + Some(secs) => client_builder + .connect_timeout(Duration::from_secs(*secs)) + .timeout(Duration::from_secs(*secs)), None => client_builder, }; @@ -650,6 +654,31 @@ impl KanidmClient { self.perform_get_request(format!("/v1/account/{}/_unix/_token", id).as_str()) } + pub fn idm_account_unix_cred_put(&self, id: &str, cred: &str) -> Result<(), ClientError> { + let req = SingleStringRequest { + value: cred.to_string(), + }; + self.perform_put_request( + format!("/v1/account/{}/_unix/_credential", id).as_str(), + req, + ) + } + + pub fn idm_account_unix_cred_delete(&self, id: &str) -> Result<(), ClientError> { + self.perform_delete_request(format!("/v1/account/{}/_unix/_credential", id).as_str()) + } + + pub fn idm_account_unix_cred_verify( + &self, + id: &str, + cred: &str, + ) -> Result, ClientError> { + let req = SingleStringRequest { + value: cred.to_string(), + }; + self.perform_post_request(format!("/v1/account/{}/_unix/_auth", id).as_str(), req) + } + pub fn idm_account_get_ssh_pubkeys(&self, id: &str) -> Result, ClientError> { self.perform_get_request(format!("/v1/account/{}/_ssh_pubkeys", id).as_str()) } diff --git a/kanidm_client/tests/proto_v1_test.rs b/kanidm_client/tests/proto_v1_test.rs index 9578cded6..4508b6c3f 100644 --- a/kanidm_client/tests/proto_v1_test.rs +++ b/kanidm_client/tests/proto_v1_test.rs @@ -15,6 +15,7 @@ use log::debug; static PORT_ALLOC: AtomicUsize = AtomicUsize::new(8080); static ADMIN_TEST_PASSWORD: &str = "integration test admin password"; static ADMIN_TEST_PASSWORD_CHANGE: &str = "integration test admin new🎉"; +static UNIX_TEST_PASSWORD: &str = "unix test user password"; // Test external behaviorus of the service. @@ -612,6 +613,67 @@ fn test_server_rest_posix_lifecycle() { }); } +#[test] +fn test_server_rest_posix_auth_lifecycle() { + run_test(|rsclient: KanidmClient| { + let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD); + assert!(res.is_ok()); + // Get an anon connection + let anon_rsclient = rsclient.new_session().unwrap(); + assert!(anon_rsclient.auth_anonymous().is_ok()); + + // Not recommended in production! + rsclient + .idm_group_add_members("idm_admins", vec!["admin"]) + .unwrap(); + + // Setup a unix user + rsclient + .idm_account_create("posix_account", "Posix Demo Account") + .unwrap(); + + // Extend the account with posix attrs. + rsclient + .idm_account_unix_extend("posix_account", None, None) + .unwrap(); + + // add their password (unix self) + rsclient + .idm_account_unix_cred_put("posix_account", UNIX_TEST_PASSWORD) + .unwrap(); + + // attempt to verify (good, anon-conn) + let r1 = anon_rsclient.idm_account_unix_cred_verify("posix_account", UNIX_TEST_PASSWORD); + match r1 { + Ok(Some(_tok)) => {} + _ => assert!(false), + }; + + // attempt to verify (bad, anon-conn) + let r2 = anon_rsclient.idm_account_unix_cred_verify("posix_account", "ntaotnhuohtsuoehtsu"); + match r2 { + Ok(None) => {} + _ => assert!(false), + }; + + // lock? (admin-conn) + // attempt to verify (good pw, should fail, anon-conn) + // status? (self-conn) + + // clear password? (unix self) + rsclient + .idm_account_unix_cred_delete("posix_account") + .unwrap(); + + // attempt to verify (good pw, should fail, anon-conn) + let r3 = anon_rsclient.idm_account_unix_cred_verify("posix_account", UNIX_TEST_PASSWORD); + match r3 { + Ok(None) => {} + _ => assert!(false), + }; + }); +} + // Test the self version of the radius path. // Test hitting all auth-required endpoints and assert they give unauthorized. diff --git a/kanidm_tools/src/main.rs b/kanidm_tools/src/main.rs index 591e042d0..07ed6165a 100644 --- a/kanidm_tools/src/main.rs +++ b/kanidm_tools/src/main.rs @@ -201,6 +201,8 @@ enum AccountPosix { Show(AccountNamedOpt), #[structopt(name = "set")] Set(AccountPosixOpt), + #[structopt(name = "set_password")] + SetPassword(AccountNamedOpt), } #[derive(Debug, StructOpt)] @@ -335,6 +337,7 @@ impl ClientOpt { AccountOpt::Posix(apopt) => match apopt { AccountPosix::Show(apo) => apo.copt.debug, AccountPosix::Set(apo) => apo.copt.debug, + AccountPosix::SetPassword(apo) => apo.copt.debug, }, AccountOpt::Ssh(asopt) => match asopt { AccountSsh::List(ano) => ano.copt.debug, @@ -527,6 +530,18 @@ fn main() { ) .unwrap(); } + AccountPosix::SetPassword(aopt) => { + let client = aopt.copt.to_client(); + let password = + rpassword::prompt_password_stderr("Enter new unix (sudo) password: ") + .unwrap(); + client + .idm_account_unix_cred_put( + aopt.aopts.account_id.as_str(), + password.as_str(), + ) + .unwrap(); + } }, // end AccountOpt::Posix AccountOpt::Ssh(asopt) => match asopt { AccountSsh::List(aopt) => { diff --git a/kanidm_unix_int/Cargo.toml b/kanidm_unix_int/Cargo.toml index 5ed43488d..0048df3ec 100644 --- a/kanidm_unix_int/Cargo.toml +++ b/kanidm_unix_int/Cargo.toml @@ -35,12 +35,19 @@ path = "src/cache_clear.rs" name = "kanidm_unixd_status" path = "src/daemon_status.rs" +[[bin]] +name = "kanidm_test_auth" +path = "src/test_auth.rs" + [dependencies] kanidm_client = { path = "../kanidm_client", version = "0.1" } kanidm_proto = { path = "../kanidm_proto", version = "0.1" } +kanidm = { path = "../kanidmd" } # actix = { path = "../../actix", version = "0.9" } actix = "0.7" # actix-rt = "1.0" +toml = "0.5" +rpassword = "0.4" tokio = { version = "0.2", features=["full"] } tokio-util = { version = "0.2", features = ["codec"] } futures = "0.3" diff --git a/kanidm_unix_int/nss_kanidm/src/lib.rs b/kanidm_unix_int/nss_kanidm/src/lib.rs index dd3ac64e9..7b32ecf75 100644 --- a/kanidm_unix_int/nss_kanidm/src/lib.rs +++ b/kanidm_unix_int/nss_kanidm/src/lib.rs @@ -4,7 +4,7 @@ extern crate libnss; extern crate lazy_static; use kanidm_unix_common::client::call_daemon_blocking; -use kanidm_unix_common::constants::DEFAULT_SOCK_PATH; +use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse, NssGroup, NssUser}; use libnss::group::{Group, GroupHooks}; @@ -18,8 +18,11 @@ libnss_passwd_hooks!(kanidm, KanidmPasswd); impl PasswdHooks for KanidmPasswd { fn get_all_entries() -> Response> { + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); let req = ClientRequest::NssAccounts; - call_daemon_blocking(DEFAULT_SOCK_PATH, req) + call_daemon_blocking(cfg.sock_path.as_str(), req) .map(|r| match r { ClientResponse::NssAccounts(l) => l.into_iter().map(passwd_from_nssuser).collect(), _ => Vec::new(), @@ -29,8 +32,11 @@ impl PasswdHooks for KanidmPasswd { } fn get_entry_by_uid(uid: libc::uid_t) -> Response { + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); let req = ClientRequest::NssAccountByUid(uid); - call_daemon_blocking(DEFAULT_SOCK_PATH, req) + call_daemon_blocking(cfg.sock_path.as_str(), req) .map(|r| match r { ClientResponse::NssAccount(opt) => opt .map(passwd_from_nssuser) @@ -42,8 +48,11 @@ impl PasswdHooks for KanidmPasswd { } fn get_entry_by_name(name: String) -> Response { + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); let req = ClientRequest::NssAccountByName(name); - call_daemon_blocking(DEFAULT_SOCK_PATH, req) + call_daemon_blocking(cfg.sock_path.as_str(), req) .map(|r| match r { ClientResponse::NssAccount(opt) => opt .map(passwd_from_nssuser) @@ -60,8 +69,11 @@ libnss_group_hooks!(kanidm, KanidmGroup); impl GroupHooks for KanidmGroup { fn get_all_entries() -> Response> { + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); let req = ClientRequest::NssGroups; - call_daemon_blocking(DEFAULT_SOCK_PATH, req) + call_daemon_blocking(cfg.sock_path.as_str(), req) .map(|r| match r { ClientResponse::NssGroups(l) => l.into_iter().map(group_from_nssgroup).collect(), _ => Vec::new(), @@ -71,8 +83,11 @@ impl GroupHooks for KanidmGroup { } fn get_entry_by_gid(gid: libc::gid_t) -> Response { + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); let req = ClientRequest::NssGroupByGid(gid); - call_daemon_blocking(DEFAULT_SOCK_PATH, req) + call_daemon_blocking(cfg.sock_path.as_str(), req) .map(|r| match r { ClientResponse::NssGroup(opt) => opt .map(group_from_nssgroup) @@ -84,8 +99,11 @@ impl GroupHooks for KanidmGroup { } fn get_entry_by_name(name: String) -> Response { + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); let req = ClientRequest::NssGroupByName(name); - call_daemon_blocking(DEFAULT_SOCK_PATH, req) + call_daemon_blocking(cfg.sock_path.as_str(), req) .map(|r| match r { ClientResponse::NssGroup(opt) => opt .map(group_from_nssgroup) diff --git a/kanidm_unix_int/pam_kanidm/Cargo.toml b/kanidm_unix_int/pam_kanidm/Cargo.toml index 2c10d8c0c..cca171971 100644 --- a/kanidm_unix_int/pam_kanidm/Cargo.toml +++ b/kanidm_unix_int/pam_kanidm/Cargo.toml @@ -11,3 +11,6 @@ path = "src/lib.rs" [dependencies] kanidm_unix_int = { path = "../", version = "0.1" } +futures = "0.3" +tokio = { version = "0.2", features=["full"] } +libc = "0.2" diff --git a/kanidm_unix_int/pam_kanidm/src/lib.rs b/kanidm_unix_int/pam_kanidm/src/lib.rs index 31e1bb209..0bdf31ed1 100644 --- a/kanidm_unix_int/pam_kanidm/src/lib.rs +++ b/kanidm_unix_int/pam_kanidm/src/lib.rs @@ -1,7 +1,296 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); +extern crate libc; + +mod pam; +use crate::pam::constants::*; +use crate::pam::conv::PamConv; +use crate::pam::module::{PamHandle, PamHooks}; + +use std::collections::BTreeSet; +use std::convert::TryFrom; +use std::ffi::CStr; +// use std::os::raw::c_char; + +// use futures::executor::block_on; +use tokio::runtime::Runtime; + +use kanidm_unix_common::client::call_daemon; +use kanidm_unix_common::unix_config::KanidmUnixdConfig; +use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; + +#[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 { + let opts: Result, _> = 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"), + }) + } +} + +fn get_cfg() -> Result { + KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .map_err(|_| PamResultCode::PAM_SERVICE_ERR) +} + +struct PamKanidm; +pam_hooks!(PamKanidm); + +impl PamHooks for PamKanidm { + 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, + }; + + if opts.debug { + println!("acct_mgmt"); + println!("args -> {:?}", args); + println!("opts -> {:?}", opts); + } + + let account_id = match pamh.get_user(None) { + Ok(aid) => aid, + Err(e) => { + if opts.debug { + println!("Error get_user -> {:?}", e); + } + return e; + } + }; + + let cfg = match get_cfg() { + Ok(cfg) => cfg, + Err(e) => return e, + }; + let req = ClientRequest::PamAccountAllowed(account_id); + // PamResultCode::PAM_IGNORE + + let mut rt = match Runtime::new() { + Ok(rt) => rt, + Err(_) => return PamResultCode::PAM_SERVICE_ERR, + }; + + match rt.block_on(call_daemon(cfg.sock_path.as_str(), req)) { + Ok(r) => match r { + ClientResponse::PamStatus(Some(true)) => { + // println!("PAM_SUCCESS"); + PamResultCode::PAM_SUCCESS + } + ClientResponse::PamStatus(Some(false)) => { + // println!("PAM_IGNORE"); + PamResultCode::PAM_AUTH_ERR + } + ClientResponse::PamStatus(None) => { + if opts.ignore_unknown_user { + PamResultCode::PAM_IGNORE + } else { + PamResultCode::PAM_USER_UNKNOWN + } + } + _ => { + // unexpected response. + if opts.debug { + println!("PAM_IGNORE -> {:?}", r); + } + PamResultCode::PAM_IGNORE + } + }, + Err(e) => { + if opts.debug { + println!("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, + }; + + if opts.debug { + println!("sm_authenticate"); + println!("args -> {:?}", args); + println!("opts -> {:?}", opts); + } + + let account_id = match pamh.get_user(None) { + Ok(aid) => aid, + Err(e) => { + println!("Error get_user -> {:?}", e); + return e; + } + }; + + let authtok = match pamh.get_authtok() { + Ok(atok) => atok, + Err(e) => { + if opts.debug { + println!("Error get_authtok -> {:?}", e); + } + return e; + } + }; + + let authtok = match authtok { + Some(v) => v, + None => { + if opts.use_first_pass { + if opts.debug { + println!("Don't have an authtok, returning PAM_AUTH_ERR"); + } + return PamResultCode::PAM_AUTH_ERR; + } else { + let conv = match pamh.get_item::() { + Ok(conv) => conv, + Err(err) => { + if opts.debug { + println!("Couldn't get pam_conv"); + } + return err; + } + }; + match conv.send(PAM_PROMPT_ECHO_OFF, "Password: ") { + Ok(password) => match password { + Some(pw) => pw, + None => { + if opts.debug { + println!("No password"); + } + return PamResultCode::PAM_CRED_INSUFFICIENT; + } + }, + Err(err) => { + if opts.debug { + println!("Couldn't get password"); + } + return err; + } + } + } // end opts.use_first_pass + } + }; + + let cfg = match get_cfg() { + Ok(cfg) => cfg, + Err(e) => return e, + }; + let req = ClientRequest::PamAuthenticate(account_id, authtok); + + let mut rt = match Runtime::new() { + Ok(rt) => rt, + Err(_) => return PamResultCode::PAM_SERVICE_ERR, + }; + + match rt.block_on(call_daemon(cfg.sock_path.as_str(), req)) { + Ok(r) => match r { + ClientResponse::PamStatus(Some(true)) => { + // println!("PAM_SUCCESS"); + PamResultCode::PAM_SUCCESS + } + ClientResponse::PamStatus(Some(false)) => { + // println!("PAM_AUTH_ERR"); + PamResultCode::PAM_AUTH_ERR + } + ClientResponse::PamStatus(None) => { + // println!("PAM_USER_UNKNOWN"); + if opts.ignore_unknown_user { + PamResultCode::PAM_IGNORE + } else { + PamResultCode::PAM_USER_UNKNOWN + } + } + _ => { + // unexpected response. + if opts.debug { + println!("PAM_IGNORE -> {:?}", r); + } + PamResultCode::PAM_IGNORE + } + }, + Err(e) => { + if opts.debug { + println!("PAM_IGNORE -> {:?}", e); + } + PamResultCode::PAM_IGNORE + } + } + } + + 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, + }; + + if opts.debug { + println!("sm_chauthtok"); + println!("args -> {:?}", args); + println!("opts -> {:?}", 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, + }; + + if opts.debug { + println!("sm_close_session"); + println!("args -> {:?}", args); + println!("opts -> {:?}", 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, + }; + + if opts.debug { + println!("sm_open_session"); + println!("args -> {:?}", args); + println!("opts -> {:?}", opts); + } + PamResultCode::PAM_SUCCESS + } + + 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, + }; + + if opts.debug { + println!("sm_setcred"); + println!("args -> {:?}", args); + println!("opts -> {:?}", opts); + } + PamResultCode::PAM_SUCCESS } } diff --git a/kanidm_unix_int/pam_kanidm/src/pam/constants.rs b/kanidm_unix_int/pam_kanidm/src/pam/constants.rs new file mode 100644 index 000000000..3f43aba65 --- /dev/null +++ b/kanidm_unix_int/pam_kanidm/src/pam/constants.rs @@ -0,0 +1,97 @@ +use libc::{c_int, c_uint}; + +// TODO: Import constants from C header file at compile time. + +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, +} diff --git a/kanidm_unix_int/pam_kanidm/src/pam/conv.rs b/kanidm_unix_int/pam_kanidm/src/pam/conv.rs new file mode 100644 index 000000000..aecc8d955 --- /dev/null +++ b/kanidm_unix_int/pam_kanidm/src/pam/conv.rs @@ -0,0 +1,85 @@ +use libc::{c_char, c_int}; +use std::ffi::{CStr, CString}; +use std::ptr; + +use crate::pam::constants::PamResultCode; +use crate::pam::constants::*; +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> { + 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 + } +} diff --git a/kanidm_unix_int/pam_kanidm/src/pam/items.rs b/kanidm_unix_int/pam_kanidm/src/pam/items.rs new file mode 100644 index 000000000..6ad1d9e02 --- /dev/null +++ b/kanidm_unix_int/pam_kanidm/src/pam/items.rs @@ -0,0 +1,78 @@ +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 + } +} diff --git a/kanidm_unix_int/pam_kanidm/src/pam/macros.rs b/kanidm_unix_int/pam_kanidm/src/pam/macros.rs new file mode 100644 index 000000000..4aa928313 --- /dev/null +++ b/kanidm_unix_int/pam_kanidm/src/pam/macros.rs @@ -0,0 +1,115 @@ +/// 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::module::{PamHooks, PamHandle}; +/// use pam::constants::{PamResultCode, PamFlag}; +/// 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) + } + } + }; +} diff --git a/kanidm_unix_int/pam_kanidm/src/pam/mod.rs b/kanidm_unix_int/pam_kanidm/src/pam/mod.rs new file mode 100755 index 000000000..ad9b6fd05 --- /dev/null +++ b/kanidm_unix_int/pam_kanidm/src/pam/mod.rs @@ -0,0 +1,32 @@ +//! 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; diff --git a/kanidm_unix_int/pam_kanidm/src/pam/module.rs b/kanidm_unix_int/pam_kanidm/src/pam/module.rs new file mode 100755 index 000000000..6a452b3c1 --- /dev/null +++ b/kanidm_unix_int/pam_kanidm/src/pam/module.rs @@ -0,0 +1,257 @@ +//! Functions for use in pam modules. + +use libc::c_char; +use std::ffi::{CStr, CString}; +use std::{mem, ptr}; + +use crate::pam::constants::{PamFlag, PamItemType, PamResultCode, PAM_AUTHTOK}; + +/// 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: 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; +} + +pub extern "C" fn cleanup(_: *const PamHandle, c_data: *mut PamDataT, _: PamResultCode) { + unsafe { + let c_data = Box::from_raw(c_data); + let data: Box = mem::transmute(c_data); + mem::drop(data); + } +} + +pub type PamResult = Result; + +/// 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 { + /// 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().as_ptr(); + let mut ptr: *const PamDataT = ptr::null(); + let res = pam_get_data(self, c_key, &mut ptr); + if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() { + let typed_ptr: *const T = mem::transmute(ptr); + 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(&self, key: &str, data: Box) -> PamResult<()> { + let c_key = CString::new(key).unwrap().as_ptr(); + let res = unsafe { + let c_data: Box = mem::transmute(data); + let c_data = Box::into_raw(c_data); + pam_set_data(self, c_key, c_data, cleanup::) + }; + 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 = mem::transmute(ptr); + 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(&mut self, item: &str) -> PamResult<()> { + let c_item = CString::new(item).unwrap().as_ptr(); + + 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 *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 { + let ptr: *mut c_char = ptr::null_mut(); + let c_prompt = match prompt { + Some(p) => CString::new(p).unwrap().as_ptr(), + None => ptr::null(), + }; + let res = unsafe { pam_get_user(self, &ptr, c_prompt) }; + 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> { + 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 = mem::transmute(ptr); + 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 + } +} diff --git a/kanidm_unix_int/pam_tester/Cargo.lock b/kanidm_unix_int/pam_tester/Cargo.lock new file mode 100644 index 000000000..0401b18a8 --- /dev/null +++ b/kanidm_unix_int/pam_tester/Cargo.lock @@ -0,0 +1,45 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "libc" +version = "0.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pam" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "pam-sys 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "users 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pam-sys" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pam_tester" +version = "0.1.0" +dependencies = [ + "pam 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "users" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +"checksum pam 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa2bdc959c201c047004a1420a92aaa1dd1a6b64d5ef333aa3a4ac764fb93097" +"checksum pam-sys 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "cd4858311a097f01a0006ef7d0cd50bca81ec430c949d7bf95cbefd202282434" +"checksum users 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fed7d0912567d35f88010c23dbaf865e9da8b5227295e8dc0f2fdd109155ab7" diff --git a/kanidm_unix_int/pam_tester/Cargo.toml b/kanidm_unix_int/pam_tester/Cargo.toml new file mode 100644 index 000000000..dca7f3c69 --- /dev/null +++ b/kanidm_unix_int/pam_tester/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "pam_tester" +version = "0.1.0" +authors = ["William Brown "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +pam = "0.7.0" + +[workspace] diff --git a/kanidm_unix_int/pam_tester/src/main.rs b/kanidm_unix_int/pam_tester/src/main.rs new file mode 100644 index 000000000..2e60fcf62 --- /dev/null +++ b/kanidm_unix_int/pam_tester/src/main.rs @@ -0,0 +1,23 @@ +extern crate pam; +pub fn main() { + let service = "pam_test"; + let user = "testuser"; + let password = "eti8aoshaigeeboh1ohF7rieba0quaThesoivae0"; + + let mut auth = pam::Authenticator::with_password(service).unwrap(); + auth.get_handler().set_credentials(user, password); + let r = auth.authenticate(); + println!("auth -> {:?}", r); + if r.is_ok() { + println!("Successfully authenticated!"); + let r = auth.open_session(); + println!("session -> {:?}", r); + if r.is_ok() { + println!("Successfully opened session!"); + } else { + println!("Session failed =/"); + } + } else { + println!("Authentication failed =/"); + } +} diff --git a/kanidm_unix_int/src/cache.rs b/kanidm_unix_int/src/cache.rs index 45f2fa2f4..a798b9ce5 100644 --- a/kanidm_unix_int/src/cache.rs +++ b/kanidm_unix_int/src/cache.rs @@ -4,6 +4,7 @@ use kanidm_client::asynchronous::KanidmAsyncClient; use kanidm_client::ClientError; use kanidm_proto::v1::{OperationError, UnixGroupToken, UnixUserToken}; use reqwest::StatusCode; +use std::collections::BTreeSet; use std::ops::Add; use std::string::ToString; use std::time::{Duration, SystemTime}; @@ -26,6 +27,7 @@ pub struct CacheLayer { db: Db, client: KanidmAsyncClient, state: Mutex, + pam_allow_groups: BTreeSet, timeout_seconds: u64, } @@ -46,6 +48,7 @@ impl CacheLayer { timeout_seconds: u64, // client: KanidmAsyncClient, + pam_allow_groups: Vec, ) -> Result { let db = Db::new(path)?; @@ -63,6 +66,7 @@ impl CacheLayer { client: client, state: Mutex::new(CacheState::OfflineNextCheck(SystemTime::now())), timeout_seconds: timeout_seconds, + pam_allow_groups: pam_allow_groups.into_iter().collect(), }) } @@ -209,6 +213,20 @@ impl CacheLayer { dbtxn.delete_group(g_uuid).and_then(|_| dbtxn.commit()) } + fn set_cache_userpassword(&self, a_uuid: &str, cred: &str) -> Result<(), ()> { + let dbtxn = self.db.write(); + dbtxn + .update_account_password(a_uuid, cred) + .and_then(|x| dbtxn.commit().map(|_| x)) + } + + fn check_cache_userpassword(&self, a_uuid: &str, cred: &str) -> Result { + let dbtxn = self.db.write(); + dbtxn + .check_account_password(a_uuid, cred) + .and_then(|x| dbtxn.commit().map(|_| x)) + } + async fn refresh_usertoken( &self, account_id: &Id, @@ -234,12 +252,28 @@ impl CacheLayer { .await; Ok(token) } + ClientError::Http( + StatusCode::UNAUTHORIZED, + Some(OperationError::NotAuthenticated), + ) => { + error!("transport unauthenticated, moving to offline"); + // Something went wrong, mark offline. + let time = SystemTime::now().add(Duration::from_secs(15)); + self.set_cachestate(CacheState::OfflineNextCheck(time)) + .await; + Ok(token) + } ClientError::Http( StatusCode::BAD_REQUEST, Some(OperationError::NoMatchingEntries), + ) + | ClientError::Http( + StatusCode::BAD_REQUEST, + Some(OperationError::InvalidAccountState(_)), ) => { - // We wele able to contact the server but the entry has been removed. - debug!("entry has been removed, clearing from cache ..."); + // We wele able to contact the server but the entry has been removed, or + // is not longer a valid posix account. + debug!("entry has been removed or is no longer a valid posix account, clearing from cache ..."); token .map(|tok| self.delete_cache_usertoken(&tok.uuid)) // Now an option> @@ -250,7 +284,7 @@ impl CacheLayer { er => { error!("client error -> {:?}", er); // Some other transient error, continue with the token. - Err(()) + Ok(token) } } } @@ -282,11 +316,26 @@ impl CacheLayer { .await; Ok(token) } + ClientError::Http( + StatusCode::UNAUTHORIZED, + Some(OperationError::NotAuthenticated), + ) => { + error!("transport unauthenticated, moving to offline"); + // Something went wrong, mark offline. + let time = SystemTime::now().add(Duration::from_secs(15)); + self.set_cachestate(CacheState::OfflineNextCheck(time)) + .await; + Ok(token) + } ClientError::Http( StatusCode::BAD_REQUEST, Some(OperationError::NoMatchingEntries), + ) + | ClientError::Http( + StatusCode::BAD_REQUEST, + Some(OperationError::InvalidAccountState(_)), ) => { - debug!("entry has been removed, clearing from cache ..."); + debug!("entry has been removed or is no longer a valid posix group, clearing from cache ..."); token .map(|tok| self.delete_cache_grouptoken(&tok.uuid)) // Now an option> @@ -297,7 +346,7 @@ impl CacheLayer { er => { error!("client error -> {:?}", er); // Some other transient error, continue with the token. - Err(()) + Ok(token) } } } @@ -497,6 +546,135 @@ impl CacheLayer { self.get_nssgroup(Id::Gid(gid)).await } + async fn online_account_authenticate( + &self, + token: &Option, + account_id: &str, + cred: &str, + ) -> Result, ()> { + debug!("Attempt online password check"); + // We are online, attempt the pw to the server. + match self + .client + .idm_account_unix_cred_verify(account_id, cred) + .await + { + Ok(Some(n_tok)) => { + debug!("online password check success."); + self.set_cache_usertoken(&n_tok)?; + self.set_cache_userpassword(&n_tok.uuid, cred)?; + Ok(Some(true)) + } + Ok(None) => { + error!("incorrect password"); + // PW failed the check. + Ok(Some(false)) + } + Err(e) => match e { + ClientError::Transport(er) => { + error!("transport error, moving to offline -> {:?}", er); + // Something went wrong, mark offline. + let time = SystemTime::now().add(Duration::from_secs(15)); + self.set_cachestate(CacheState::OfflineNextCheck(time)) + .await; + token + .as_ref() + .map(|t| self.check_cache_userpassword(&t.uuid, cred)) + .transpose() + } + ClientError::Http( + StatusCode::UNAUTHORIZED, + Some(OperationError::NotAuthenticated), + ) => { + error!("transport unauthenticated, moving to offline"); + // Something went wrong, mark offline. + let time = SystemTime::now().add(Duration::from_secs(15)); + self.set_cachestate(CacheState::OfflineNextCheck(time)) + .await; + token + .as_ref() + .map(|t| self.check_cache_userpassword(&t.uuid, cred)) + .transpose() + } + ClientError::Http( + StatusCode::BAD_REQUEST, + Some(OperationError::NoMatchingEntries), + ) + | ClientError::Http( + StatusCode::BAD_REQUEST, + Some(OperationError::InvalidAccountState(_)), + ) => { + error!("unknown account or is not a valid posix account"); + Ok(None) + } + er => { + error!("client error -> {:?}", er); + // Some other unknown processing error? + Err(()) + } + }, + } + } + + fn offline_account_authenticate( + &self, + token: &Option, + cred: &str, + ) -> Result, ()> { + debug!("Attempt offline password check"); + token + .as_ref() + .map(|t| self.check_cache_userpassword(&t.uuid, cred)) + .transpose() + } + + pub async fn pam_account_allowed(&self, account_id: &str) -> Result, ()> { + let token = self.get_usertoken(Id::Name(account_id.to_string())).await?; + + Ok(token.map(|tok| { + let user_set: BTreeSet<_> = tok.groups.iter().map(|g| g.name.clone()).collect(); + + debug!( + "Checking if -> {:?} & {:?}", + user_set, self.pam_allow_groups + ); + + let b = user_set.intersection(&self.pam_allow_groups).count() > 0; + b + })) + } + + pub async fn pam_account_authenticate( + &self, + account_id: &str, + cred: &str, + ) -> Result, ()> { + let state = self.get_cachestate().await; + let (_expired, token) = self.get_cached_usertoken(&Id::Name(account_id.to_string()))?; + + match state { + CacheState::Online => { + self.online_account_authenticate(&token, account_id, cred) + .await + } + CacheState::OfflineNextCheck(time) => { + // Should this always attempt to go online? + if SystemTime::now() >= time && self.test_connection().await { + // Brought ourselves online, lets check. + self.online_account_authenticate(&token, account_id, cred) + .await + } else { + // We are offline, check from the cache if possible. + self.offline_account_authenticate(&token, cred) + } + } + _ => { + // We are offline, check from the cache if possible. + self.offline_account_authenticate(&token, cred) + } + } + } + pub async fn test_connection(&self) -> bool { let state = self.get_cachestate().await; match state { diff --git a/kanidm_unix_int/src/cache_clear.rs b/kanidm_unix_int/src/cache_clear.rs index 70c220ac7..6b59c00ca 100644 --- a/kanidm_unix_int/src/cache_clear.rs +++ b/kanidm_unix_int/src/cache_clear.rs @@ -7,7 +7,7 @@ use structopt::StructOpt; use futures::executor::block_on; use kanidm_unix_common::client::call_daemon; -use kanidm_unix_common::constants::DEFAULT_SOCK_PATH; +use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; #[derive(Debug, StructOpt)] @@ -30,14 +30,18 @@ async fn main() { debug!("Starting cache invalidate tool ..."); + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); + if !opt.really { error!("Are you sure you want to proceed? If so use --really"); return; } - let req = ClientRequest::InvalidateCache; + let req = ClientRequest::ClearCache; - match block_on(call_daemon(DEFAULT_SOCK_PATH, req)) { + match block_on(call_daemon(cfg.sock_path.as_str(), req)) { Ok(r) => match r { ClientResponse::Ok => info!("success"), _ => { diff --git a/kanidm_unix_int/src/cache_invalidate.rs b/kanidm_unix_int/src/cache_invalidate.rs index fa535bbed..1bc8d7a2d 100644 --- a/kanidm_unix_int/src/cache_invalidate.rs +++ b/kanidm_unix_int/src/cache_invalidate.rs @@ -7,7 +7,7 @@ use structopt::StructOpt; use futures::executor::block_on; use kanidm_unix_common::client::call_daemon; -use kanidm_unix_common::constants::DEFAULT_SOCK_PATH; +use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; #[derive(Debug, StructOpt)] @@ -27,9 +27,14 @@ async fn main() { env_logger::init(); debug!("Starting cache invalidate tool ..."); + + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); + let req = ClientRequest::InvalidateCache; - match block_on(call_daemon(DEFAULT_SOCK_PATH, req)) { + match block_on(call_daemon(cfg.sock_path.as_str(), req)) { Ok(r) => match r { ClientResponse::Ok => info!("success"), _ => { diff --git a/kanidm_unix_int/src/daemon.rs b/kanidm_unix_int/src/daemon.rs index 29dea0b8c..585a7717d 100644 --- a/kanidm_unix_int/src/daemon.rs +++ b/kanidm_unix_int/src/daemon.rs @@ -15,9 +15,7 @@ use tokio_util::codec::{Decoder, Encoder}; use kanidm_client::KanidmClientBuilder; use kanidm_unix_common::cache::CacheLayer; -use kanidm_unix_common::constants::{ - DEFAULT_CACHE_TIMEOUT, DEFAULT_CONN_TIMEOUT, DEFAULT_DB_PATH, DEFAULT_SOCK_PATH, -}; +use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; //=== the codec @@ -45,11 +43,11 @@ impl Encoder for ClientCodec { type Error = io::Error; fn encode(&mut self, msg: ClientResponse, dst: &mut BytesMut) -> Result<(), Self::Error> { + debug!("Attempting to send response -> {:?} ...", msg); let data = serde_cbor::to_vec(&msg).map_err(|e| { error!("socket encoding error -> {:?}", e); io::Error::new(io::ErrorKind::Other, "CBOR encode error") })?; - debug!("Attempting to send response -> {:?} ...", data); dst.put(data.as_slice()); Ok(()) } @@ -153,6 +151,22 @@ async fn handle_client( ClientResponse::NssGroup(None) }) } + ClientRequest::PamAuthenticate(account_id, cred) => { + debug!("pam authenticate"); + cachelayer + .pam_account_authenticate(account_id.as_str(), cred.as_str()) + .await + .map(|r| ClientResponse::PamStatus(r)) + .unwrap_or(ClientResponse::Error) + } + ClientRequest::PamAccountAllowed(account_id) => { + debug!("pam account allowed"); + cachelayer + .pam_account_allowed(account_id.as_str()) + .await + .map(|r| ClientResponse::PamStatus(r)) + .unwrap_or(ClientResponse::Error) + } ClientRequest::InvalidateCache => { debug!("invalidate cache"); cachelayer @@ -190,29 +204,35 @@ async fn handle_client( async fn main() { // ::std::env::set_var("RUST_LOG", "kanidm=debug,kanidm_client=debug"); env_logger::init(); - rm_if_exist(DEFAULT_SOCK_PATH); // setup let cb = KanidmClientBuilder::new() .read_options_from_optional_config("/etc/kanidm/config") .expect("Failed to parse /etc/kanidm/config"); - let cb = cb.connect_timeout(DEFAULT_CONN_TIMEOUT); + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); + + rm_if_exist(cfg.sock_path.as_str()); + + let cb = cb.connect_timeout(cfg.conn_timeout); let rsclient = cb.build_async().expect("Failed to build async client"); let cachelayer = Arc::new( CacheLayer::new( - DEFAULT_DB_PATH, // The sqlite db path - DEFAULT_CACHE_TIMEOUT, + cfg.db_path.as_str(), // The sqlite db path + cfg.cache_timeout, rsclient, + cfg.pam_allowed_login_groups.clone(), ) .expect("Failed to build cache layer."), ); // Set the umask while we open the path let before = unsafe { umask(0) }; - let mut listener = UnixListener::bind(DEFAULT_SOCK_PATH).unwrap(); + let mut listener = UnixListener::bind(cfg.sock_path.as_str()).unwrap(); // Undo it. let _ = unsafe { umask(before) }; diff --git a/kanidm_unix_int/src/daemon_status.rs b/kanidm_unix_int/src/daemon_status.rs index e20ee4f94..f00f4a642 100644 --- a/kanidm_unix_int/src/daemon_status.rs +++ b/kanidm_unix_int/src/daemon_status.rs @@ -7,7 +7,7 @@ use structopt::StructOpt; use futures::executor::block_on; use kanidm_unix_common::client::call_daemon; -use kanidm_unix_common::constants::DEFAULT_SOCK_PATH; +use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; #[derive(Debug, StructOpt)] @@ -27,9 +27,14 @@ async fn main() { env_logger::init(); debug!("Starting cache invalidate tool ..."); + + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); + let req = ClientRequest::Status; - match block_on(call_daemon(DEFAULT_SOCK_PATH, req)) { + match block_on(call_daemon(cfg.sock_path.as_str(), req)) { Ok(r) => match r { ClientResponse::Ok => info!("working!"), _ => { diff --git a/kanidm_unix_int/src/db.rs b/kanidm_unix_int/src/db.rs index 540ff4291..eb2cf938a 100644 --- a/kanidm_unix_int/src/db.rs +++ b/kanidm_unix_int/src/db.rs @@ -8,6 +8,9 @@ use std::fmt; use crate::cache::Id; use std::sync::{Mutex, MutexGuard}; +use kanidm::be::dbvalue::DbPasswordV1; +use kanidm::credential::Password; + pub struct Db { pool: Pool, lock: Mutex<()>, @@ -137,7 +140,7 @@ impl<'a> DbTxn<'a> { .execute("COMMIT TRANSACTION", NO_PARAMS) .map(|_| ()) .map_err(|e| { - debug!("sqlite commit failure -> {:?}", e); + error!("sqlite commit failure -> {:?}", e); () }) } @@ -146,14 +149,14 @@ impl<'a> DbTxn<'a> { self.conn .execute("UPDATE group_t SET expiry = 0", NO_PARAMS) .map_err(|e| { - debug!("sqlite update group_t failure -> {:?}", e); + error!("sqlite update group_t failure -> {:?}", e); () })?; self.conn .execute("UPDATE account_t SET expiry = 0", NO_PARAMS) .map_err(|e| { - debug!("sqlite update account_t failure -> {:?}", e); + error!("sqlite update account_t failure -> {:?}", e); () })?; @@ -164,14 +167,14 @@ impl<'a> DbTxn<'a> { self.conn .execute("DELETE FROM group_t", NO_PARAMS) .map_err(|e| { - debug!("sqlite delete group_t failure -> {:?}", e); + error!("sqlite delete group_t failure -> {:?}", e); () })?; self.conn .execute("DELETE FROM account_t", NO_PARAMS) .map_err(|e| { - debug!("sqlite delete group_t failure -> {:?}", e); + error!("sqlite delete group_t failure -> {:?}", e); () })?; @@ -292,7 +295,6 @@ impl<'a> DbTxn<'a> { data.iter() .map(|token| { // token convert with cbor. - debug!("{:?}", token); serde_cbor::from_slice(token.as_slice()).map_err(|e| { error!("cbor error -> {:?}", e); () @@ -311,6 +313,23 @@ impl<'a> DbTxn<'a> { () })?; + // This isn't needed because insert or replace into seems to clean up dups! + /* + self.conn.execute_named("DELETE FROM account_t WHERE NOT uuid = :uuid AND (name = :name OR spn = :spn OR gidnumber = :gidnumber)", + &[ + (":uuid", &account.uuid), + (":name", &account.name), + (":spn", &account.spn), + (":gidnumber", &account.gidnumber), + ] + ) + .map_err(|e| { + debug!("sqlite delete account_t duplicate failure -> {:?}", e); + () + }) + .map(|_| ()) + */ + let mut stmt = self.conn .prepare("INSERT OR REPLACE INTO account_t (uuid, name, spn, gidnumber, token, expiry) VALUES (:uuid, :name, :spn, :gidnumber, :token, :expiry)") .map_err(|e| { @@ -386,6 +405,78 @@ impl<'a> DbTxn<'a> { }) } + pub fn update_account_password(&self, a_uuid: &str, cred: &str) -> Result<(), ()> { + let pw = Password::new(cred); + let dbpw = pw.to_dbpasswordv1(); + let data = serde_cbor::to_vec(&dbpw).map_err(|e| { + error!("cbor error -> {:?}", e); + () + })?; + + self.conn + .execute_named( + "UPDATE account_t SET password = :data WHERE uuid = :a_uuid", + &[(":a_uuid", &a_uuid), (":data", &data)], + ) + .map_err(|e| { + error!("sqlite update account_t password failure -> {:?}", e); + () + }) + .map(|_| ()) + } + + pub fn check_account_password(&self, a_uuid: &str, cred: &str) -> Result { + let mut stmt = self + .conn + .prepare("SELECT password FROM account_t WHERE uuid = :a_uuid AND password IS NOT NULL") + .map_err(|e| { + error!("sqlite select prepare failure -> {:?}", e); + () + })?; + + // Makes tuple (token, expiry) + let data_iter = stmt + .query_map(&[a_uuid], |row| Ok(row.get(0)?)) + .map_err(|e| { + error!("sqlite query_map failure -> {:?}", e); + () + })?; + let data: Result>, _> = data_iter + .map(|v| { + v.map_err(|e| { + error!("sqlite map failure -> {:?}", e); + () + }) + }) + .collect(); + + let data = data?; + + if data.len() == 0 { + info!("No cached password, failing authentication"); + return Ok(false); + } + + if data.len() >= 2 { + error!("invalid db state, multiple entries matched query?"); + return Err(()); + } + + let r: Result = data + .first() + .map(|raw| { + // Map the option from data.first. + let dbpw: DbPasswordV1 = serde_cbor::from_slice(raw.as_slice()).map_err(|e| { + error!("cbor error -> {:?}", e); + () + })?; + let pw = Password::try_from(dbpw)?; + Ok(pw.verify(cred)) + }) + .unwrap_or(Ok(false)); + r + } + fn get_group_data_name(&self, grp_id: &str) -> Result, i64)>, ()> { let mut stmt = self.conn .prepare( @@ -500,7 +591,7 @@ impl<'a> DbTxn<'a> { data.iter() .map(|token| { // token convert with cbor. - debug!("{:?}", token); + // debug!("{:?}", token); serde_cbor::from_slice(token.as_slice()).map_err(|e| { error!("cbor error -> {:?}", e); () @@ -538,7 +629,7 @@ impl<'a> DbTxn<'a> { data.iter() .map(|token| { // token convert with cbor. - debug!("{:?}", token); + // debug!("{:?}", token); serde_cbor::from_slice(token.as_slice()).map_err(|e| { error!("cbor error -> {:?}", e); () @@ -617,6 +708,9 @@ mod tests { use crate::cache::Id; use kanidm_proto::v1::{UnixGroupToken, UnixUserToken}; + static TESTACCOUNT1_PASSWORD_A: &str = "password a for account1 test"; + static TESTACCOUNT1_PASSWORD_B: &str = "password b for account1 test"; + #[test] fn test_cache_db_account_basic() { let _ = env_logger::builder().is_test(true).try_init(); @@ -841,4 +935,165 @@ mod tests { assert!(dbtxn.commit().is_ok()); } + + #[test] + fn test_cache_db_account_password() { + let _ = env_logger::builder().is_test(true).try_init(); + let db = Db::new("").expect("failed to create."); + let dbtxn = db.write(); + assert!(dbtxn.migrate().is_ok()); + + let uuid1 = "0302b99c-f0f6-41ab-9492-852692b0fd16"; + let ut1 = UnixUserToken { + name: "testuser".to_string(), + spn: "testuser@example.com".to_string(), + displayname: "Test User".to_string(), + gidnumber: 2000, + uuid: "0302b99c-f0f6-41ab-9492-852692b0fd16".to_string(), + shell: None, + groups: Vec::new(), + sshkeys: vec!["key-a".to_string()], + }; + + // Test that with no account, is false + assert!(dbtxn.check_account_password(uuid1, TESTACCOUNT1_PASSWORD_A) == Ok(false)); + // test adding an account + dbtxn.update_account(&ut1, 0).unwrap(); + // check with no password is false. + assert!(dbtxn.check_account_password(uuid1, TESTACCOUNT1_PASSWORD_A) == Ok(false)); + // update the pw + assert!(dbtxn + .update_account_password(uuid1, TESTACCOUNT1_PASSWORD_A) + .is_ok()); + // Check it now works. + assert!(dbtxn.check_account_password(uuid1, TESTACCOUNT1_PASSWORD_A) == Ok(true)); + assert!(dbtxn.check_account_password(uuid1, TESTACCOUNT1_PASSWORD_B) == Ok(false)); + // Update the pw + assert!(dbtxn + .update_account_password(uuid1, TESTACCOUNT1_PASSWORD_B) + .is_ok()); + // Check it matches. + assert!(dbtxn.check_account_password(uuid1, TESTACCOUNT1_PASSWORD_A) == Ok(false)); + assert!(dbtxn.check_account_password(uuid1, TESTACCOUNT1_PASSWORD_B) == Ok(true)); + + assert!(dbtxn.commit().is_ok()); + } + + #[test] + fn test_cache_db_group_rename_duplicate() { + let _ = env_logger::builder().is_test(true).try_init(); + let db = Db::new("").expect("failed to create."); + let dbtxn = db.write(); + assert!(dbtxn.migrate().is_ok()); + + let mut gt1 = UnixGroupToken { + name: "testgroup".to_string(), + spn: "testgroup@example.com".to_string(), + gidnumber: 2000, + uuid: "0302b99c-f0f6-41ab-9492-852692b0fd16".to_string(), + }; + + let gt2 = UnixGroupToken { + name: "testgroup".to_string(), + spn: "testgroup@example.com".to_string(), + gidnumber: 2001, + uuid: "799123b2-3802-4b19-b0b8-1ffae2aa9a4b".to_string(), + }; + + let id_name = Id::Name("testgroup".to_string()); + let id_name2 = Id::Name("testgroup2".to_string()); + + // test finding no group + let r1 = dbtxn.get_group(&id_name).unwrap(); + assert!(r1.is_none()); + + // test adding a group + dbtxn.update_group(>1, 0).unwrap(); + let r0 = dbtxn.get_group(&id_name).unwrap(); + assert!(r0.unwrap().0.uuid == "0302b99c-f0f6-41ab-9492-852692b0fd16"); + + // Do the "rename" of gt1 which is what would allow gt2 to be valid. + gt1.name = "testgroup2".to_string(); + gt1.spn = "testgroup2@example.com".to_string(); + // Now, add gt2 which dups on gt1 name/spn. + dbtxn.update_group(>2, 0).unwrap(); + let r2 = dbtxn.get_group(&id_name).unwrap(); + assert!(r2.unwrap().0.uuid == "799123b2-3802-4b19-b0b8-1ffae2aa9a4b"); + let r3 = dbtxn.get_group(&id_name2).unwrap(); + assert!(r3.is_none()); + + // Now finally update gt1 + dbtxn.update_group(>1, 0).unwrap(); + + // Both now coexist + let r4 = dbtxn.get_group(&id_name).unwrap(); + assert!(r4.unwrap().0.uuid == "799123b2-3802-4b19-b0b8-1ffae2aa9a4b"); + let r5 = dbtxn.get_group(&id_name2).unwrap(); + assert!(r5.unwrap().0.uuid == "0302b99c-f0f6-41ab-9492-852692b0fd16"); + + assert!(dbtxn.commit().is_ok()); + } + + #[test] + fn test_cache_db_account_rename_duplicate() { + let _ = env_logger::builder().is_test(true).try_init(); + let db = Db::new("").expect("failed to create."); + let dbtxn = db.write(); + assert!(dbtxn.migrate().is_ok()); + + let mut ut1 = UnixUserToken { + name: "testuser".to_string(), + spn: "testuser@example.com".to_string(), + displayname: "Test User".to_string(), + gidnumber: 2000, + uuid: "0302b99c-f0f6-41ab-9492-852692b0fd16".to_string(), + shell: None, + groups: Vec::new(), + sshkeys: vec!["key-a".to_string()], + }; + + let ut2 = UnixUserToken { + name: "testuser".to_string(), + spn: "testuser@example.com".to_string(), + displayname: "Test User".to_string(), + gidnumber: 2001, + uuid: "799123b2-3802-4b19-b0b8-1ffae2aa9a4b".to_string(), + shell: None, + groups: Vec::new(), + sshkeys: vec!["key-a".to_string()], + }; + + let id_name = Id::Name("testuser".to_string()); + let id_name2 = Id::Name("testuser2".to_string()); + + // test finding no account + let r1 = dbtxn.get_account(&id_name).unwrap(); + assert!(r1.is_none()); + + // test adding an account + dbtxn.update_account(&ut1, 0).unwrap(); + let r0 = dbtxn.get_account(&id_name).unwrap(); + assert!(r0.unwrap().0.uuid == "0302b99c-f0f6-41ab-9492-852692b0fd16"); + + // Do the "rename" of gt1 which is what would allow gt2 to be valid. + ut1.name = "testuser2".to_string(); + ut1.spn = "testuser2@example.com".to_string(); + // Now, add gt2 which dups on gt1 name/spn. + dbtxn.update_account(&ut2, 0).unwrap(); + let r2 = dbtxn.get_account(&id_name).unwrap(); + assert!(r2.unwrap().0.uuid == "799123b2-3802-4b19-b0b8-1ffae2aa9a4b"); + let r3 = dbtxn.get_account(&id_name2).unwrap(); + assert!(r3.is_none()); + + // Now finally update gt1 + dbtxn.update_account(&ut1, 0).unwrap(); + + // Both now coexist + let r4 = dbtxn.get_account(&id_name).unwrap(); + assert!(r4.unwrap().0.uuid == "799123b2-3802-4b19-b0b8-1ffae2aa9a4b"); + let r5 = dbtxn.get_account(&id_name2).unwrap(); + assert!(r5.unwrap().0.uuid == "0302b99c-f0f6-41ab-9492-852692b0fd16"); + + assert!(dbtxn.commit().is_ok()); + } } diff --git a/kanidm_unix_int/src/lib.rs b/kanidm_unix_int/src/lib.rs index 4714c97bc..3d64899d7 100644 --- a/kanidm_unix_int/src/lib.rs +++ b/kanidm_unix_int/src/lib.rs @@ -8,6 +8,7 @@ extern crate log; pub mod cache; pub mod client; -pub mod constants; +mod constants; pub(crate) mod db; +pub mod unix_config; pub mod unix_proto; diff --git a/kanidm_unix_int/src/ssh_authorizedkeys.rs b/kanidm_unix_int/src/ssh_authorizedkeys.rs index f913f750f..25e619bfc 100644 --- a/kanidm_unix_int/src/ssh_authorizedkeys.rs +++ b/kanidm_unix_int/src/ssh_authorizedkeys.rs @@ -7,7 +7,7 @@ use structopt::StructOpt; use futures::executor::block_on; use kanidm_unix_common::client::call_daemon; -use kanidm_unix_common::constants::DEFAULT_SOCK_PATH; +use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; #[derive(Debug, StructOpt)] @@ -29,9 +29,14 @@ async fn main() { env_logger::init(); debug!("Starting authorized keys tool ..."); + + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); + let req = ClientRequest::SshKey(opt.account_id.clone()); - match block_on(call_daemon(DEFAULT_SOCK_PATH, req)) { + match block_on(call_daemon(cfg.sock_path.as_str(), req)) { Ok(r) => match r { ClientResponse::SshKeys(sk) => sk.iter().for_each(|k| { println!("{}", k); diff --git a/kanidm_unix_int/src/test_auth.rs b/kanidm_unix_int/src/test_auth.rs new file mode 100644 index 000000000..50bb3594e --- /dev/null +++ b/kanidm_unix_int/src/test_auth.rs @@ -0,0 +1,83 @@ +#[macro_use] +extern crate log; + +use log::debug; +use structopt::StructOpt; + +use futures::executor::block_on; + +use kanidm_unix_common::client::call_daemon; +use kanidm_unix_common::unix_config::KanidmUnixdConfig; +use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; + +#[derive(Debug, StructOpt)] +struct ClientOpt { + #[structopt(short = "d", long = "debug")] + debug: bool, + #[structopt(short = "D", long = "name")] + account_id: String, +} + +#[tokio::main] +async fn main() { + let opt = ClientOpt::from_args(); + if opt.debug { + ::std::env::set_var("RUST_LOG", "kanidm=debug,kanidm_client=debug"); + } else { + ::std::env::set_var("RUST_LOG", "kanidm=info,kanidm_client=info"); + } + env_logger::init(); + + debug!("Starting cache invalidate tool ..."); + + let cfg = KanidmUnixdConfig::new() + .read_options_from_optional_config("/etc/kanidm/unixd") + .expect("Failed to parse /etc/kanidm/unixd"); + + let password = rpassword::prompt_password_stderr("Enter unix password: ").unwrap(); + + let req = ClientRequest::PamAuthenticate(opt.account_id.clone(), password); + let sereq = ClientRequest::PamAccountAllowed(opt.account_id.clone()); + + match block_on(call_daemon(cfg.sock_path.as_str(), req)) { + Ok(r) => match r { + ClientResponse::PamStatus(Some(true)) => { + info!("auth success!"); + } + ClientResponse::PamStatus(Some(false)) => { + info!("auth failed!"); + } + ClientResponse::PamStatus(None) => { + info!("user unknown"); + } + _ => { + // unexpected response. + error!("Error: unexpected response -> {:?}", r); + } + }, + Err(e) => { + error!("Error -> {:?}", e); + } + } + + match block_on(call_daemon(cfg.sock_path.as_str(), sereq)) { + Ok(r) => match r { + ClientResponse::PamStatus(Some(true)) => { + info!("auth success!"); + } + ClientResponse::PamStatus(Some(false)) => { + info!("auth failed!"); + } + ClientResponse::PamStatus(None) => { + info!("user unknown"); + } + _ => { + // unexpected response. + error!("Error: unexpected response -> {:?}", r); + } + }, + Err(e) => { + error!("Error -> {:?}", e); + } + } +} diff --git a/kanidm_unix_int/src/unix_config.rs b/kanidm_unix_int/src/unix_config.rs new file mode 100644 index 000000000..773b4ceab --- /dev/null +++ b/kanidm_unix_int/src/unix_config.rs @@ -0,0 +1,68 @@ +use crate::constants::{ + DEFAULT_CACHE_TIMEOUT, DEFAULT_CONN_TIMEOUT, DEFAULT_DB_PATH, DEFAULT_SOCK_PATH, +}; +use serde_derive::Deserialize; +use std::fs::File; +use std::io::Read; +use std::path::Path; + +#[derive(Debug, Deserialize)] +struct ConfigInt { + db_path: Option, + sock_path: Option, + conn_timeout: Option, + cache_timeout: Option, + pam_allowed_login_groups: Option>, +} + +#[derive(Debug)] +pub struct KanidmUnixdConfig { + pub db_path: String, + pub sock_path: String, + pub conn_timeout: u64, + pub cache_timeout: u64, + pub pam_allowed_login_groups: Vec, +} + +impl KanidmUnixdConfig { + pub fn new() -> Self { + KanidmUnixdConfig { + db_path: DEFAULT_DB_PATH.to_string(), + sock_path: DEFAULT_SOCK_PATH.to_string(), + conn_timeout: DEFAULT_CONN_TIMEOUT, + cache_timeout: DEFAULT_CACHE_TIMEOUT, + pam_allowed_login_groups: Vec::new(), + } + } + + pub fn read_options_from_optional_config>( + self, + config_path: P, + ) -> Result { + let mut f = match File::open(config_path) { + Ok(f) => f, + Err(e) => { + debug!("Unabled to open config file [{:?}], skipping ...", e); + return Ok(self); + } + }; + + let mut contents = String::new(); + f.read_to_string(&mut contents) + .map_err(|e| eprintln!("{:?}", e))?; + + let config: ConfigInt = + toml::from_str(contents.as_str()).map_err(|e| eprintln!("{:?}", e))?; + + // Now map the values into our config. + Ok(KanidmUnixdConfig { + db_path: config.db_path.unwrap_or(self.db_path), + sock_path: config.sock_path.unwrap_or(self.sock_path), + conn_timeout: config.conn_timeout.unwrap_or(self.conn_timeout), + cache_timeout: config.cache_timeout.unwrap_or(self.cache_timeout), + pam_allowed_login_groups: config + .pam_allowed_login_groups + .unwrap_or(self.pam_allowed_login_groups), + }) + } +} diff --git a/kanidm_unix_int/src/unix_proto.rs b/kanidm_unix_int/src/unix_proto.rs index f192d4c38..d88316ef7 100644 --- a/kanidm_unix_int/src/unix_proto.rs +++ b/kanidm_unix_int/src/unix_proto.rs @@ -23,6 +23,8 @@ pub enum ClientRequest { NssGroups, NssGroupByGid(u32), NssGroupByName(String), + PamAuthenticate(String, String), + PamAccountAllowed(String), InvalidateCache, ClearCache, Status, @@ -35,6 +37,7 @@ pub enum ClientResponse { NssAccount(Option), NssGroups(Vec), NssGroup(Option), + PamStatus(Option), Ok, Error, } diff --git a/kanidm_unix_int/tests/cache_layer_test.rs b/kanidm_unix_int/tests/cache_layer_test.rs index 57c0a1e25..a069db653 100644 --- a/kanidm_unix_int/tests/cache_layer_test.rs +++ b/kanidm_unix_int/tests/cache_layer_test.rs @@ -14,6 +14,9 @@ use kanidm_client::{KanidmClient, KanidmClientBuilder}; static PORT_ALLOC: AtomicUsize = AtomicUsize::new(18080); static ADMIN_TEST_PASSWORD: &str = "integration test admin password"; +static TESTACCOUNT1_PASSWORD_A: &str = "password a for account1 test"; +static TESTACCOUNT1_PASSWORD_B: &str = "password b for account1 test"; +static TESTACCOUNT1_PASSWORD_INC: &str = "never going to work"; fn run_test(fix_fn: fn(&KanidmClient) -> (), test_fn: fn(CacheLayer, KanidmAsyncClient) -> ()) { // ::std::env::set_var("RUST_LOG", "actix_web=debug,kanidm=debug"); @@ -64,7 +67,9 @@ fn run_test(fix_fn: fn(&KanidmClient) -> (), test_fn: fn(CacheLayer, KanidmAsync let cachelayer = CacheLayer::new( "", // The sqlite db path, this is in memory. - 300, rsclient, + 300, + rsclient, + vec!["allowed_group".to_string()], ) .expect("Failed to build cache layer."); @@ -97,6 +102,10 @@ fn test_fixture(rsclient: &KanidmClient) -> () { .idm_account_post_ssh_pubkey("testaccount1", "tk", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAeGW1P6Pc2rPq0XqbRaDKBcXZUPRklo0L1EyR30CwoP william@amethyst") .unwrap(); + // Set a posix password + rsclient + .idm_account_unix_cred_put("testaccount1", TESTACCOUNT1_PASSWORD_A) + .unwrap(); // Setup a group rsclient.idm_group_create("testgroup1").unwrap(); @@ -106,6 +115,12 @@ fn test_fixture(rsclient: &KanidmClient) -> () { rsclient .idm_group_unix_extend("testgroup1", Some(20001)) .unwrap(); + + // Setup the allowed group + rsclient.idm_group_create("allowed_group").unwrap(); + rsclient + .idm_group_unix_extend("allowed_group", Some(20002)) + .unwrap(); } #[test] @@ -341,3 +356,163 @@ fn test_cache_account_delete() { rt.block_on(fut); }) } + +#[test] +fn test_cache_account_password() { + run_test(test_fixture, |cachelayer, adminclient| { + let mut rt = Runtime::new().expect("Failed to start tokio"); + let fut = async move { + cachelayer.attempt_online().await; + // Test authentication failure. + let a1 = cachelayer + .pam_account_authenticate("testaccount1", TESTACCOUNT1_PASSWORD_INC) + .await + .expect("failed to authenticate"); + assert!(a1 == Some(false)); + + // Test authentication success. + let a2 = cachelayer + .pam_account_authenticate("testaccount1", TESTACCOUNT1_PASSWORD_A) + .await + .expect("failed to authenticate"); + assert!(a2 == Some(true)); + + // change pw + adminclient + .auth_simple_password("admin", ADMIN_TEST_PASSWORD) + .await + .expect("failed to auth as admin"); + adminclient + .idm_account_unix_cred_put("testaccount1", TESTACCOUNT1_PASSWORD_B) + .await + .expect("Failed to change password"); + + // test auth (old pw) fail + let a3 = cachelayer + .pam_account_authenticate("testaccount1", TESTACCOUNT1_PASSWORD_A) + .await + .expect("failed to authenticate"); + assert!(a3 == Some(false)); + + // test auth (new pw) success + let a4 = cachelayer + .pam_account_authenticate("testaccount1", TESTACCOUNT1_PASSWORD_B) + .await + .expect("failed to authenticate"); + assert!(a4 == Some(true)); + + // Go offline. + cachelayer.mark_offline().await; + + // Test auth success + let a5 = cachelayer + .pam_account_authenticate("testaccount1", TESTACCOUNT1_PASSWORD_B) + .await + .expect("failed to authenticate"); + assert!(a5 == Some(true)); + + // Test auth failure. + let a6 = cachelayer + .pam_account_authenticate("testaccount1", TESTACCOUNT1_PASSWORD_INC) + .await + .expect("failed to authenticate"); + assert!(a6 == Some(false)); + + // clear cache + cachelayer.clear_cache().expect("failed to clear cache"); + + // test auth good (fail) + let a7 = cachelayer + .pam_account_authenticate("testaccount1", TESTACCOUNT1_PASSWORD_B) + .await + .expect("failed to authenticate"); + assert!(a7 == None); + + // go online + cachelayer.attempt_online().await; + assert!(cachelayer.test_connection().await); + + // test auth success + let a8 = cachelayer + .pam_account_authenticate("testaccount1", TESTACCOUNT1_PASSWORD_B) + .await + .expect("failed to authenticate"); + assert!(a8 == Some(true)); + }; + rt.block_on(fut); + }) +} + +#[test] +fn test_cache_account_pam_allowed() { + run_test(test_fixture, |cachelayer, adminclient| { + let mut rt = Runtime::new().expect("Failed to start tokio"); + let fut = async move { + cachelayer.attempt_online().await; + + // Should fail + let a1 = cachelayer + .pam_account_allowed("testaccount1") + .await + .expect("failed to authenticate"); + assert!(a1 == Some(false)); + + adminclient + .auth_simple_password("admin", ADMIN_TEST_PASSWORD) + .await + .expect("failed to auth as admin"); + adminclient + .idm_group_add_members("allowed_group", vec!["testaccount1"]) + .await + .unwrap(); + + // Invalidate cache to force a refresh + assert!(cachelayer.invalidate().is_ok()); + + // Should pass + let a2 = cachelayer + .pam_account_allowed("testaccount1") + .await + .expect("failed to authenticate"); + assert!(a2 == Some(true)); + }; + rt.block_on(fut); + }) +} + +#[test] +fn test_cache_account_pam_nonexist() { + run_test(test_fixture, |cachelayer, _adminclient| { + let mut rt = Runtime::new().expect("Failed to start tokio"); + let fut = async move { + cachelayer.attempt_online().await; + + let a1 = cachelayer + .pam_account_allowed("NO_SUCH_ACCOUNT") + .await + .expect("failed to authenticate"); + assert!(a1 == None); + + let a2 = cachelayer + .pam_account_authenticate("NO_SUCH_ACCOUNT", TESTACCOUNT1_PASSWORD_B) + .await + .expect("failed to authenticate"); + assert!(a2 == None); + + cachelayer.mark_offline().await; + + let a1 = cachelayer + .pam_account_allowed("NO_SUCH_ACCOUNT") + .await + .expect("failed to authenticate"); + assert!(a1 == None); + + let a2 = cachelayer + .pam_account_authenticate("NO_SUCH_ACCOUNT", TESTACCOUNT1_PASSWORD_B) + .await + .expect("failed to authenticate"); + assert!(a2 == None); + }; + rt.block_on(fut); + }) +} diff --git a/kanidmd/Dockerfile b/kanidmd/Dockerfile index a48fdd50c..fa2d17c96 100644 --- a/kanidmd/Dockerfile +++ b/kanidmd/Dockerfile @@ -7,7 +7,7 @@ RUN zypper mr -d repo-non-oss && \ zypper ar https://download.opensuse.org/update/tumbleweed/ repo-update-https && \ zypper ar https://download.opensuse.org/tumbleweed/repo/oss/ repo-oss-https && \ zypper ar https://download.opensuse.org/tumbleweed/repo/non-oss/ repo-non-oss-https && \ - zypper install -y timezone cargo rust gcc sqlite3-devel libopenssl-devel + zypper install -y timezone cargo rust gcc sqlite3-devel libopenssl-devel pam-devel COPY . /home/kanidm/ WORKDIR /home/kanidm/ RUN cargo build --release diff --git a/kanidmd/src/lib/actors/v1_read.rs b/kanidmd/src/lib/actors/v1_read.rs index ed5237f5c..ea8a5a8b4 100644 --- a/kanidmd/src/lib/actors/v1_read.rs +++ b/kanidmd/src/lib/actors/v1_read.rs @@ -4,7 +4,9 @@ use crate::audit::AuditScope; use crate::async_log::EventLog; use crate::event::{AuthEvent, SearchEvent, SearchResult, WhoamiResult}; -use crate::idm::event::{RadiusAuthTokenEvent, UnixGroupTokenEvent, UnixUserTokenEvent}; +use crate::idm::event::{ + RadiusAuthTokenEvent, UnixGroupTokenEvent, UnixUserAuthEvent, UnixUserTokenEvent, +}; use crate::value::PartialValue; use kanidm_proto::v1::{OperationError, RadiusAuthToken}; @@ -139,6 +141,16 @@ impl Message for InternalSshKeyTagReadMessage { type Result = Result, OperationError>; } +pub struct IdmAccountUnixAuthMessage { + pub uat: Option, + pub uuid_or_name: String, + pub cred: String, +} + +impl Message for IdmAccountUnixAuthMessage { + type Result = Result, OperationError>; +} + // =========================================================== pub struct QueryServerReadV1 { @@ -653,3 +665,54 @@ impl Handler for QueryServerReadV1 { res } } + +impl Handler for QueryServerReadV1 { + type Result = Result, OperationError>; + + fn handle(&mut self, msg: IdmAccountUnixAuthMessage, _: &mut Self::Context) -> Self::Result { + let mut audit = AuditScope::new("idm_account_unix_auth"); + let res = audit_segment!(&mut audit, || { + let mut idm_write = self.idms.write(); + + // resolve the id + let target_uuid = Uuid::parse_str(msg.uuid_or_name.as_str()).or_else(|_| { + idm_write + .qs_read + .posixid_to_uuid(&mut audit, msg.uuid_or_name.as_str()) + .map_err(|e| { + audit_log!(&mut audit, "Error resolving as gidnumber continuing ..."); + e + }) + })?; + // Make an event from the request + let uuae = match UnixUserAuthEvent::from_parts( + &mut audit, + &idm_write.qs_read, + msg.uat, + target_uuid, + msg.cred, + ) { + Ok(s) => s, + Err(e) => { + audit_log!(audit, "Failed to begin unix auth: {:?}", e); + return Err(e); + } + }; + + audit_log!(audit, "Begin event {:?}", uuae); + + let ct = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Clock failure!"); + + let r = idm_write + .auth_unix(&mut audit, &uuae, ct) + .and_then(|r| idm_write.commit().map(|_| r)); + + audit_log!(audit, "Sending result -> {:?}", r); + r + }); + self.log.do_send(audit); + res + } +} diff --git a/kanidmd/src/lib/actors/v1_write.rs b/kanidmd/src/lib/actors/v1_write.rs index 94c87573e..db69c7990 100644 --- a/kanidmd/src/lib/actors/v1_write.rs +++ b/kanidmd/src/lib/actors/v1_write.rs @@ -5,7 +5,10 @@ use crate::async_log::EventLog; use crate::event::{ CreateEvent, DeleteEvent, ModifyEvent, PurgeRecycledEvent, PurgeTombstoneEvent, }; -use crate::idm::event::{GeneratePasswordEvent, PasswordChangeEvent, RegenerateRadiusSecretEvent}; +use crate::idm::event::{ + GeneratePasswordEvent, PasswordChangeEvent, RegenerateRadiusSecretEvent, + UnixPasswordChangeEvent, +}; use crate::modify::{Modify, ModifyInvalid, ModifyList}; use crate::value::{PartialValue, Value}; use kanidm_proto::v1::OperationError; @@ -148,6 +151,16 @@ impl Message for IdmGroupUnixExtendMessage { type Result = Result<(), OperationError>; } +pub struct IdmAccountUnixSetCredMessage { + pub uat: Option, + pub uuid_or_name: String, + pub cred: String, +} + +impl Message for IdmAccountUnixSetCredMessage { + type Result = Result<(), OperationError>; +} + pub struct InternalCredentialSetMessage { pub uat: Option, pub uuid_or_name: String, @@ -869,6 +882,45 @@ impl Handler for QueryServerWriteV1 { } } +impl Handler for QueryServerWriteV1 { + type Result = Result<(), OperationError>; + + fn handle(&mut self, msg: IdmAccountUnixSetCredMessage, _: &mut Self::Context) -> Self::Result { + let mut audit = AuditScope::new("idm_account_unix_set_cred"); + let res = audit_segment!(&mut audit, || { + let mut idms_prox_write = self.idms.proxy_write(); + + let target_uuid = Uuid::parse_str(msg.uuid_or_name.as_str()).or_else(|_| { + idms_prox_write + .qs_write + .posixid_to_uuid(&mut audit, msg.uuid_or_name.as_str()) + .map_err(|e| { + audit_log!(&mut audit, "Error resolving as gidnumber continuing ..."); + e + }) + })?; + + let upce = UnixPasswordChangeEvent::from_parts( + &mut audit, + &idms_prox_write.qs_write, + msg.uat, + target_uuid, + msg.cred, + ) + .map_err(|e| { + audit_log!(audit, "Failed to begin UnixPasswordChangeEvent: {:?}", e); + e + })?; + idms_prox_write + .set_unix_account_password(&mut audit, &upce) + .and_then(|_| idms_prox_write.commit(&mut audit)) + .map(|_| ()) + }); + self.log.do_send(audit); + res + } +} + // These below are internal only types. impl Handler for QueryServerWriteV1 { diff --git a/kanidmd/src/lib/constants/mod.rs b/kanidmd/src/lib/constants/mod.rs index 0b6b72ed5..43fd69782 100644 --- a/kanidmd/src/lib/constants/mod.rs +++ b/kanidmd/src/lib/constants/mod.rs @@ -5,7 +5,7 @@ pub mod system_config; pub use crate::constants::system_config::JSON_SYSTEM_CONFIG_V1; // Increment this as we add new schema types and values!!! -pub static SYSTEM_INDEX_VERSION: i64 = 4; +pub static SYSTEM_INDEX_VERSION: i64 = 5; // On test builds, define to 60 seconds #[cfg(test)] pub static PURGE_TIMEOUT: u64 = 60; @@ -109,6 +109,7 @@ pub static UUID_SCHEMA_CLASS_POSIXGROUP: &str = "00000000-0000-0000-0000-ffff000 pub static UUID_SCHEMA_ATTR_BADLIST_PASSWORD: &str = "00000000-0000-0000-0000-ffff00000059"; pub static UUID_SCHEMA_CLASS_SYSTEM_CONFIG: &str = "00000000-0000-0000-0000-ffff00000060"; pub static UUID_SCHEMA_ATTR_LOGINSHELL: &str = "00000000-0000-0000-0000-ffff00000061"; +pub static UUID_SCHEMA_ATTR_UNIX_PASSWORD: &str = "00000000-0000-0000-0000-ffff00000062"; // System and domain infos // I'd like to strongly criticise william of the past for fucking up these allocations. @@ -591,10 +592,10 @@ pub static JSON_IDM_SELF_ACP_WRITE_V1: &str = r#"{ "\"Self\"" ], "acp_modify_removedattr": [ - "name", "displayname", "legalname", "radius_secret", "primary_credential", "ssh_publickey" + "name", "displayname", "legalname", "radius_secret", "primary_credential", "ssh_publickey", "unix_password" ], "acp_modify_presentattr": [ - "name", "displayname", "legalname", "radius_secret", "primary_credential", "ssh_publickey" + "name", "displayname", "legalname", "radius_secret", "primary_credential", "ssh_publickey", "unix_password" ] } }"#; @@ -1301,13 +1302,13 @@ pub static JSON_IDM_ACP_ACCOUNT_UNIX_EXTEND_PRIV_V1: &str = r#"{ "{\"And\": [{\"Eq\": [\"class\",\"account\"]}, {\"AndNot\": {\"Or\": [{\"Eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"Eq\": [\"class\", \"tombstone\"]}, {\"Eq\": [\"class\", \"recycled\"]}]}}]}" ], "acp_search_attr": [ - "class", "name", "spn", "uuid", "description", "gidnumber", "loginshell" + "class", "name", "spn", "uuid", "description", "gidnumber", "loginshell", "unix_password" ], "acp_modify_removedattr": [ - "class", "loginshell", "gidnumber" + "class", "loginshell", "gidnumber", "unix_password" ], "acp_modify_presentattr": [ - "class", "loginshell", "gidnumber" + "class", "loginshell", "gidnumber", "unix_password" ], "acp_modify_class": ["posixaccount"] } @@ -1733,6 +1734,35 @@ pub static JSON_SCHEMA_ATTR_LOGINSHELL: &str = r#"{ } }"#; +pub static JSON_SCHEMA_ATTR_UNIX_PASSWORD: &str = r#"{ + "attrs": { + "class": [ + "object", + "system", + "attributetype" + ], + "description": [ + "A posix users unix login password." + ], + "index": [], + "unique": [ + "false" + ], + "multivalue": [ + "false" + ], + "attributename": [ + "unix_password" + ], + "syntax": [ + "CREDENTIAL" + ], + "uuid": [ + "00000000-0000-0000-0000-ffff00000062" + ] + } +}"#; + pub static JSON_SCHEMA_CLASS_PERSON: &str = r#" { "valid": { @@ -1901,7 +1931,8 @@ pub static JSON_SCHEMA_CLASS_POSIXACCOUNT: &str = r#" "posixaccount" ], "systemmay": [ - "loginshell" + "loginshell", + "unix_password" ], "systemmust": [ "gidnumber" diff --git a/kanidmd/src/lib/core.rs b/kanidmd/src/lib/core.rs index a76a3fe7b..c1f1449d6 100644 --- a/kanidmd/src/lib/core.rs +++ b/kanidmd/src/lib/core.rs @@ -16,16 +16,18 @@ use crate::config::Configuration; // SearchResult use crate::actors::v1_read::QueryServerReadV1; use crate::actors::v1_read::{ - AuthMessage, InternalRadiusReadMessage, InternalRadiusTokenReadMessage, InternalSearchMessage, - InternalSshKeyReadMessage, InternalSshKeyTagReadMessage, InternalUnixGroupTokenReadMessage, + AuthMessage, IdmAccountUnixAuthMessage, InternalRadiusReadMessage, + InternalRadiusTokenReadMessage, InternalSearchMessage, InternalSshKeyReadMessage, + InternalSshKeyTagReadMessage, InternalUnixGroupTokenReadMessage, InternalUnixUserTokenReadMessage, SearchMessage, WhoamiMessage, }; use crate::actors::v1_write::QueryServerWriteV1; use crate::actors::v1_write::{ AppendAttributeMessage, CreateMessage, DeleteMessage, IdmAccountSetPasswordMessage, - IdmAccountUnixExtendMessage, IdmGroupUnixExtendMessage, InternalCredentialSetMessage, - InternalDeleteMessage, InternalRegenerateRadiusMessage, InternalSshKeyCreateMessage, - ModifyMessage, PurgeAttributeMessage, RemoveAttributeValueMessage, SetAttributeMessage, + IdmAccountUnixExtendMessage, IdmAccountUnixSetCredMessage, IdmGroupUnixExtendMessage, + InternalCredentialSetMessage, InternalDeleteMessage, InternalRegenerateRadiusMessage, + InternalSshKeyCreateMessage, ModifyMessage, PurgeAttributeMessage, RemoveAttributeValueMessage, + SetAttributeMessage, }; use crate::async_log; use crate::audit::AuditScope; @@ -954,6 +956,119 @@ fn account_get_id_unix_token( Box::new(res) } +fn account_post_id_unix_auth( + (path, req, state): (Path, HttpRequest, State), +) -> impl Future { + let max_size = state.max_size; + let uat = get_current_user(&req); + let id = path.into_inner(); + req.payload() + .from_err() + .fold(BytesMut::new(), move |mut body, chunk| { + // limit max size of in-memory payload + if (body.len() + chunk.len()) > max_size { + Err(error::ErrorBadRequest("overflow")) + } else { + body.extend_from_slice(&chunk); + Ok(body) + } + }) + // `Future::and_then` can be used to merge an asynchronous workflow with a + // synchronous workflow + .and_then( + move |body| -> Box> { + let r_obj = serde_json::from_slice::(&body); + + match r_obj { + Ok(obj) => { + let m_obj = IdmAccountUnixAuthMessage { + uat: uat, + uuid_or_name: id, + cred: obj.value, + }; + let res = state.qe_r.send(m_obj).from_err().and_then(|res| match res { + Ok(event_result) => Ok(HttpResponse::Ok().json(event_result)), + Err(e) => Ok(operation_error_to_response(e)), + }); + + Box::new(res) + } + Err(e) => Box::new(future::err(error::ErrorBadRequest(format!( + "Json Decode Failed: {:?}", + e + )))), + } // end match + }, + ) // end and_then +} + +fn account_put_id_unix_credential( + path: Path, + req: HttpRequest, + state: State, +) -> impl Future { + let max_size = state.max_size; + let uat = get_current_user(&req); + let id = path.into_inner(); + + req.payload() + .from_err() + .fold(BytesMut::new(), move |mut body, chunk| { + // limit max size of in-memory payload + if (body.len() + chunk.len()) > max_size { + Err(error::ErrorBadRequest("overflow")) + } else { + body.extend_from_slice(&chunk); + Ok(body) + } + }) + .and_then( + move |body| -> Box> { + let r_obj = serde_json::from_slice::(&body); + match r_obj { + Ok(obj) => { + let m_obj = IdmAccountUnixSetCredMessage { + uat, + uuid_or_name: id, + cred: obj.value, + }; + let res = state.qe_w.send(m_obj).from_err().and_then(|res| match res { + Ok(_) => Ok(HttpResponse::Ok().json(())), + Err(e) => Ok(operation_error_to_response(e)), + }); + + Box::new(res) + } + Err(e) => Box::new(future::err(error::ErrorBadRequest(format!( + "Json Decode Failed: {:?}", + e + )))), + } // end match + }, + ) // end and_then +} + +fn account_delete_id_unix_credential( + (path, req, state): (Path, HttpRequest, State), +) -> impl Future { + let uat = get_current_user(&req); + let id = path.into_inner(); + + let obj = PurgeAttributeMessage { + uat, + uuid_or_name: id, + attr: "unix_password".to_string(), + filter: filter_all!(f_eq("class", PartialValue::new_class("posixaccount"))), + }; + + let res = state.qe_w.send(obj).from_err().and_then(|res| match res { + Ok(()) => Ok(HttpResponse::Ok().json(())), + Err(e) => Ok(operation_error_to_response(e)), + }); + + Box::new(res) +} + fn group_get( (req, state): (HttpRequest, State), ) -> impl Future { @@ -1875,6 +1990,16 @@ pub fn create_server_core(config: Configuration) { r.method(http::Method::GET) .with_async(account_get_id_unix_token) }) + .resource("/v1/account/{id}/_unix/_auth", |r| { + r.method(http::Method::POST) + .with_async(account_post_id_unix_auth) + }) + .resource("/v1/account/{id}/_unix/_credential", |r| { + r.method(http::Method::PUT) + .with_async(account_put_id_unix_credential); + r.method(http::Method::DELETE) + .with_async(account_delete_id_unix_credential); + }) // People // Groups .resource("/v1/group", |r| { diff --git a/kanidmd/src/lib/credential.rs b/kanidmd/src/lib/credential.rs index 50b5e3066..de5ef3892 100644 --- a/kanidmd/src/lib/credential.rs +++ b/kanidmd/src/lib/credential.rs @@ -92,6 +92,14 @@ impl Password { } } } + + pub fn to_dbpasswordv1(&self) -> DbPasswordV1 { + match &self.material { + KDF::PBKDF2(cost, salt, hash) => { + DbPasswordV1::PBKDF2(*cost, salt.clone(), hash.clone()) + } + } + } } #[derive(Clone, Debug)] @@ -173,11 +181,7 @@ impl Credential { pub fn to_db_valuev1(&self) -> DbCredV1 { DbCredV1 { password: match &self.password { - Some(pw) => match &pw.material { - KDF::PBKDF2(cost, salt, hash) => { - Some(DbPasswordV1::PBKDF2(*cost, salt.clone(), hash.clone())) - } - }, + Some(pw) => Some(pw.to_dbpasswordv1()), None => None, }, claims: self.claims.clone(), diff --git a/kanidmd/src/lib/entry.rs b/kanidmd/src/lib/entry.rs index 9bca082af..6f91fde73 100644 --- a/kanidmd/src/lib/entry.rs +++ b/kanidmd/src/lib/entry.rs @@ -6,11 +6,13 @@ //! with no ordering. An entry has many avas. A pseudo example, minus schema and typing: //! //! ``` +//! /* //! Entry { //! "name": ["william"], //! "uuid": ["..."], //! "mail": ["maila@example.com", "mailb@example.com"], //! } +//! */ //! ``` //! //! There are three rules for entries: diff --git a/kanidmd/src/lib/idm/event.rs b/kanidmd/src/lib/idm/event.rs index c602f6b84..930ea7842 100644 --- a/kanidmd/src/lib/idm/event.rs +++ b/kanidmd/src/lib/idm/event.rs @@ -60,6 +60,40 @@ impl PasswordChangeEvent { } } +#[derive(Debug)] +pub struct UnixPasswordChangeEvent { + pub event: Event, + pub target: Uuid, + pub cleartext: String, +} + +impl UnixPasswordChangeEvent { + #[cfg(test)] + pub fn new_internal(target: &Uuid, cleartext: &str) -> Self { + UnixPasswordChangeEvent { + event: Event::from_internal(), + target: *target, + cleartext: cleartext.to_string(), + } + } + + pub fn from_parts( + audit: &mut AuditScope, + qs: &QueryServerWriteTransaction, + uat: Option, + target: Uuid, + cleartext: String, + ) -> Result { + let e = Event::from_rw_uat(audit, qs, uat)?; + + Ok(UnixPasswordChangeEvent { + event: e, + target, + cleartext, + }) + } +} + #[derive(Debug)] pub struct GeneratePasswordEvent { pub event: Event, @@ -188,3 +222,37 @@ impl UnixGroupTokenEvent { UnixGroupTokenEvent { event: e, target } } } + +#[derive(Debug)] +pub struct UnixUserAuthEvent { + pub event: Event, + pub target: Uuid, + pub cleartext: String, +} + +impl UnixUserAuthEvent { + #[cfg(test)] + pub fn new_internal(target: &Uuid, cleartext: &str) -> Self { + UnixUserAuthEvent { + event: Event::from_internal(), + target: *target, + cleartext: cleartext.to_string(), + } + } + + pub fn from_parts( + audit: &mut AuditScope, + qs: &QueryServerReadTransaction, + uat: Option, + target: Uuid, + cleartext: String, + ) -> Result { + let e = Event::from_ro_uat(audit, qs, uat)?; + + Ok(UnixUserAuthEvent { + event: e, + target, + cleartext, + }) + } +} diff --git a/kanidmd/src/lib/idm/server.rs b/kanidmd/src/lib/idm/server.rs index 549d73317..3578218d2 100644 --- a/kanidmd/src/lib/idm/server.rs +++ b/kanidmd/src/lib/idm/server.rs @@ -6,7 +6,7 @@ use crate::idm::account::Account; use crate::idm::authsession::AuthSession; use crate::idm::event::{ GeneratePasswordEvent, PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent, - UnixGroupTokenEvent, UnixUserTokenEvent, + UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent, }; use crate::idm::radius::RadiusAccount; use crate::idm::unix::{UnixGroup, UnixUserAccount}; @@ -43,7 +43,8 @@ pub struct IdmServerWriteTransaction<'a> { // the idm in memory structures (maybe the query server too). This is // things like authentication sessions: BptreeMapWriteTxn<'a, Uuid, AuthSession>, - qs: &'a QueryServer, + pub qs_read: QueryServerReadTransaction, + // qs: &'a QueryServer, sid: &'a SID, } @@ -72,7 +73,8 @@ impl IdmServer { pub fn write(&self) -> IdmServerWriteTransaction { IdmServerWriteTransaction { sessions: self.sessions.write(), - qs: &self.qs, + // qs: &self.qs, + qs_read: self.qs.read(), sid: &self.sid, } } @@ -132,7 +134,7 @@ impl<'a> IdmServerWriteTransaction<'a> { // // We *DO NOT* need a write though, because I think that lock outs // and rate limits are *per server* and *in memory* only. - let qs_read = self.qs.read(); + // // Check anything needed? Get the current auth-session-id from request // because it associates to the nonce's etc which were all cached. @@ -144,7 +146,7 @@ impl<'a> IdmServerWriteTransaction<'a> { ])); // Get the first / single entry we expect here .... - let entry = match qs_read.internal_search(au, filter_entry) { + let entry = match self.qs_read.internal_search(au, filter_entry) { Ok(mut entries) => { // Get only one entry out ... if entries.len() >= 2 { @@ -164,7 +166,7 @@ impl<'a> IdmServerWriteTransaction<'a> { // typing and functionality so we can assess what auth types can // continue, and helps to keep non-needed entry specific data // out of the LRU. - let account = Account::try_from_entry_ro(au, entry, &qs_read)?; + let account = Account::try_from_entry_ro(au, entry, &self.qs_read)?; let auth_session = AuthSession::new(account, init.appid.clone()); // Get the set of mechanisms that can proceed. This is tied @@ -180,7 +182,7 @@ impl<'a> IdmServerWriteTransaction<'a> { self.sessions.insert(sessionid, auth_session); // Debugging: ensure we really inserted ... - assert!(self.sessions.get(&sessionid).is_some()); + debug_assert!(self.sessions.get(&sessionid).is_some()); Ok(AuthResult { sessionid, @@ -210,6 +212,27 @@ impl<'a> IdmServerWriteTransaction<'a> { } } + pub fn auth_unix( + &mut self, + au: &mut AuditScope, + uae: &UnixUserAuthEvent, + _ct: Duration, + ) -> Result, OperationError> { + // TODO #59: Implement soft lock checking for unix creds here! + + // Get the entry/target we are working on. + let account_entry = try_audit!(au, self.qs_read.internal_search_uuid(au, &uae.target)); + + // Get their account + let account = try_audit!( + au, + UnixUserAccount::try_from_entry_ro(au, account_entry, &self.qs_read) + ); + + // Validate the unix_pw - this checks the account/cred lock states. + account.verify_unix_credential(au, uae.cleartext.as_str()) + } + pub fn commit(self) -> Result<(), OperationError> { self.sessions.commit(); Ok(()) @@ -271,51 +294,26 @@ impl IdmServerProxyReadTransaction { } impl<'a> IdmServerProxyWriteTransaction<'a> { - pub fn set_account_password( - &mut self, + fn check_password_quality( + &self, au: &mut AuditScope, - pce: &PasswordChangeEvent, + cleartext: &str, + related_inputs: &[&str], ) -> Result<(), OperationError> { - // Get the account - let account_entry = try_audit!(au, self.qs_write.internal_search_uuid(au, &pce.target)); - let account = try_audit!( - au, - Account::try_from_entry_rw(au, account_entry, &self.qs_write) - ); - // Ask if tis all good - this step checks pwpolicy and such - - // Deny the change if the account is anonymous! - if account.is_anonymous() { - return Err(OperationError::SystemProtectedObject); - } - - // Question: Is it a security issue to reveal pw policy checks BEFORE permission is - // determined over the credential modification? - // - // I don't think so - because we should only be showing how STRONG the pw is ... - // password strength and badlisting is always global, rather than per-pw-policy. // pw-policy as check on the account is about requirements for mfa for example. // // is the password at least 10 char? - if pce.cleartext.len() < PW_MIN_LENGTH { + if cleartext.len() < PW_MIN_LENGTH { return Err(OperationError::PasswordTooShort(PW_MIN_LENGTH)); } // does the password pass zxcvbn? - // Get related inputs, such as account name, email, etc. - let related: Vec<&str> = vec![ - account.name.as_str(), - account.displayname.as_str(), - account.spn.as_str(), - ]; - let entropy = try_audit!( au, - zxcvbn::zxcvbn(pce.cleartext.as_str(), related.as_slice()) - .map_err(|_| OperationError::PasswordEmpty) + zxcvbn::zxcvbn(cleartext, related_inputs).map_err(|_| OperationError::PasswordEmpty) ); // check account pwpolicy (for 3 or 4)? Do we need pw strength beyond this @@ -341,7 +339,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { // check a password badlist to eliminate more content // we check the password as "lower case" to help eliminate possibilities - let lc_password = PartialValue::new_iutf8s(pce.cleartext.as_str()); + let lc_password = PartialValue::new_iutf8s(cleartext); let badlist_entry = try_audit!( au, self.qs_write.internal_search_uuid(au, &UUID_SYSTEM_CONFIG) @@ -351,6 +349,44 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { return Err(OperationError::PasswordBadListed); } + Ok(()) + } + + pub fn set_account_password( + &mut self, + au: &mut AuditScope, + pce: &PasswordChangeEvent, + ) -> Result<(), OperationError> { + // Get the account + let account_entry = try_audit!(au, self.qs_write.internal_search_uuid(au, &pce.target)); + let account = try_audit!( + au, + Account::try_from_entry_rw(au, account_entry, &self.qs_write) + ); + // Ask if tis all good - this step checks pwpolicy and such + + // Deny the change if the account is anonymous! + if account.is_anonymous() { + return Err(OperationError::SystemProtectedObject); + } + + // Question: Is it a security issue to reveal pw policy checks BEFORE permission is + // determined over the credential modification? + // + // I don't think so - because we should only be showing how STRONG the pw is ... + + // Get related inputs, such as account name, email, etc. + let related_inputs: Vec<&str> = vec![ + account.name.as_str(), + account.displayname.as_str(), + account.spn.as_str(), + ]; + + try_audit!( + au, + self.check_password_quality(au, pce.cleartext.as_str(), related_inputs.as_slice()) + ); + // it returns a modify let modlist = try_audit!( au, @@ -375,6 +411,58 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { Ok(()) } + pub fn set_unix_account_password( + &mut self, + au: &mut AuditScope, + pce: &UnixPasswordChangeEvent, + ) -> Result<(), OperationError> { + // Get the account + let account_entry = try_audit!(au, self.qs_write.internal_search_uuid(au, &pce.target)); + // Assert the account is unix and valid. + let account = try_audit!( + au, + UnixUserAccount::try_from_entry_rw(au, account_entry, &self.qs_write) + ); + // Ask if tis all good - this step checks pwpolicy and such + + // Deny the change if the account is anonymous! + if account.is_anonymous() { + return Err(OperationError::SystemProtectedObject); + } + + // Get related inputs, such as account name, email, etc. + let related_inputs: Vec<&str> = vec![ + account.name.as_str(), + account.displayname.as_str(), + account.spn.as_str(), + ]; + + try_audit!( + au, + self.check_password_quality(au, pce.cleartext.as_str(), related_inputs.as_slice()) + ); + + // it returns a modify + let modlist = try_audit!(au, account.gen_password_mod(pce.cleartext.as_str())); + audit_log!(au, "processing change {:?}", modlist); + // given the new credential generate a modify + // We use impersonate here to get the event from ae + try_audit!( + au, + self.qs_write.impersonate_modify( + au, + // Filter as executed + filter!(f_eq("uuid", PartialValue::new_uuidr(&pce.target))), + // Filter as intended (acp) + filter_all!(f_eq("uuid", PartialValue::new_uuidr(&pce.target))), + modlist, + &pce.event, + ) + ); + + Ok(()) + } + pub fn recover_account( &mut self, au: &mut AuditScope, @@ -493,7 +581,7 @@ mod tests { use crate::event::{AuthEvent, AuthResult, CreateEvent, ModifyEvent}; use crate::idm::event::{ PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent, - UnixGroupTokenEvent, UnixUserTokenEvent, + UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent, }; use crate::modify::{Modify, ModifyList}; use crate::value::{PartialValue, Value}; @@ -934,4 +1022,66 @@ mod tests { assert!(tok_g.spn == "admin@example.com"); }) } + + #[test] + fn test_idm_simple_unix_password_reset() { + run_idm_test!(|_qs: &QueryServer, idms: &IdmServer, au: &mut AuditScope| { + let mut idms_prox_write = idms.proxy_write(); + // make the admin a valid posix account + let me_posix = unsafe { + ModifyEvent::new_internal_invalid( + filter!(f_eq("name", PartialValue::new_iutf8s("admin"))), + ModifyList::new_list(vec![ + Modify::Present("class".to_string(), Value::new_class("posixaccount")), + Modify::Present("gidnumber".to_string(), Value::new_uint32(2001)), + ]), + ) + }; + assert!(idms_prox_write.qs_write.modify(au, &me_posix).is_ok()); + + let pce = UnixPasswordChangeEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD); + + assert!(idms_prox_write.set_unix_account_password(au, &pce).is_ok()); + assert!(idms_prox_write.set_unix_account_password(au, &pce).is_ok()); + assert!(idms_prox_write.commit(au).is_ok()); + + let mut idms_write = idms.write(); + // Check auth verification of the password + + let uuae_good = UnixUserAuthEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD); + let a1 = idms_write.auth_unix(au, &uuae_good, Duration::from_secs(TEST_CURRENT_TIME)); + match a1 { + Ok(Some(_tok)) => {} + _ => assert!(false), + }; + // Check bad password + let uuae_bad = UnixUserAuthEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD_INC); + let a2 = idms_write.auth_unix(au, &uuae_bad, Duration::from_secs(TEST_CURRENT_TIME)); + match a2 { + Ok(None) => {} + _ => assert!(false), + }; + assert!(idms_write.commit().is_ok()); + + // Check deleting the password + let mut idms_prox_write = idms.proxy_write(); + let me_purge_up = unsafe { + ModifyEvent::new_internal_invalid( + filter!(f_eq("name", PartialValue::new_iutf8s("admin"))), + ModifyList::new_list(vec![Modify::Purged("unix_password".to_string())]), + ) + }; + assert!(idms_prox_write.qs_write.modify(au, &me_purge_up).is_ok()); + assert!(idms_prox_write.commit(au).is_ok()); + + // And auth should now fail due to the lack of PW material + let mut idms_write = idms.write(); + let a3 = idms_write.auth_unix(au, &uuae_good, Duration::from_secs(TEST_CURRENT_TIME)); + match a3 { + Ok(None) => {} + _ => assert!(false), + }; + assert!(idms_write.commit().is_ok()); + }) + } } diff --git a/kanidmd/src/lib/idm/unix.rs b/kanidmd/src/lib/idm/unix.rs index 814b096fd..65ad5e183 100644 --- a/kanidmd/src/lib/idm/unix.rs +++ b/kanidmd/src/lib/idm/unix.rs @@ -1,9 +1,14 @@ use uuid::Uuid; use crate::audit::AuditScope; +use crate::constants::UUID_ANONYMOUS; +use crate::credential::Credential; use crate::entry::{Entry, EntryCommitted, EntryReduced, EntryValid}; -use crate::server::{QueryServerReadTransaction, QueryServerTransaction}; -use crate::value::PartialValue; +use crate::modify::{ModifyInvalid, ModifyList}; +use crate::server::{ + QueryServerReadTransaction, QueryServerTransaction, QueryServerWriteTransaction, +}; +use crate::value::{PartialValue, Value}; use kanidm_proto::v1::OperationError; use kanidm_proto::v1::{UnixGroupToken, UnixUserToken}; @@ -19,6 +24,7 @@ pub(crate) struct UnixUserAccount { pub shell: Option, pub sshkeys: Vec, pub groups: Vec, + cred: Option, } lazy_static! { @@ -28,50 +34,48 @@ lazy_static! { static ref PVCLASS_POSIXGROUP: PartialValue = PartialValue::new_class("posixgroup"); } -impl UnixUserAccount { - pub(crate) fn try_from_entry_reduced( - au: &mut AuditScope, - value: Entry, - qs: &QueryServerReadTransaction, - ) -> Result { - if !value.attribute_value_pres("class", &PVCLASS_ACCOUNT) { +macro_rules! try_from_entry { + ($value:expr, $groups:expr) => {{ + if !$value.attribute_value_pres("class", &PVCLASS_ACCOUNT) { return Err(OperationError::InvalidAccountState( "Missing class: account".to_string(), )); } - if !value.attribute_value_pres("class", &PVCLASS_POSIXACCOUNT) { + if !$value.attribute_value_pres("class", &PVCLASS_POSIXACCOUNT) { return Err(OperationError::InvalidAccountState( "Missing class: posixaccount".to_string(), )); } - let name = value.get_ava_single_string("name").ok_or_else(|| { + let name = $value.get_ava_single_string("name").ok_or_else(|| { OperationError::InvalidAccountState("Missing attribute: name".to_string()) })?; - let spn = value + let spn = $value .get_ava_single("spn") .map(|v| v.to_proto_string_clone()) .ok_or_else(|| { OperationError::InvalidAccountState("Missing attribute: spn".to_string()) })?; - let uuid = *value.get_uuid(); + let uuid = *$value.get_uuid(); - let displayname = value.get_ava_single_string("displayname").ok_or_else(|| { + let displayname = $value.get_ava_single_string("displayname").ok_or_else(|| { OperationError::InvalidAccountState("Missing attribute: displayname".to_string()) })?; - let gidnumber = value.get_ava_single_uint32("gidnumber").ok_or_else(|| { + let gidnumber = $value.get_ava_single_uint32("gidnumber").ok_or_else(|| { OperationError::InvalidAccountState("Missing attribute: gidnumber".to_string()) })?; - let shell = value.get_ava_single_string("loginshell"); + let shell = $value.get_ava_single_string("loginshell"); - let sshkeys = value.get_ava_ssh_pubkeys("ssh_publickey"); + let sshkeys = $value.get_ava_ssh_pubkeys("ssh_publickey"); - let groups = UnixGroup::try_from_account_entry_red_ro(au, &value, qs)?; + let cred = $value + .get_ava_single_credential("unix_password") + .map(|v| v.clone()); Ok(UnixUserAccount { name, @@ -81,8 +85,38 @@ impl UnixUserAccount { gidnumber, shell, sshkeys, - groups, + groups: $groups, + cred, }) + }}; +} + +impl UnixUserAccount { + pub(crate) fn try_from_entry_rw( + au: &mut AuditScope, + value: Entry, + qs: &QueryServerWriteTransaction, + ) -> Result { + let groups = UnixGroup::try_from_account_entry_rw(au, &value, qs)?; + try_from_entry!(value, groups) + } + + pub(crate) fn try_from_entry_ro( + au: &mut AuditScope, + value: Entry, + qs: &QueryServerReadTransaction, + ) -> Result { + let groups = UnixGroup::try_from_account_entry_ro(au, &value, qs)?; + try_from_entry!(value, groups) + } + + pub(crate) fn try_from_entry_reduced( + au: &mut AuditScope, + value: Entry, + qs: &QueryServerReadTransaction, + ) -> Result { + let groups = UnixGroup::try_from_account_entry_red_ro(au, &value, qs)?; + try_from_entry!(value, groups) } pub(crate) fn to_unixusertoken(&self) -> Result { @@ -100,6 +134,46 @@ impl UnixUserAccount { sshkeys: self.sshkeys.clone(), }) } + + pub fn is_anonymous(&self) -> bool { + self.uuid == *UUID_ANONYMOUS + } + + pub(crate) fn gen_password_mod( + &self, + cleartext: &str, + ) -> Result, OperationError> { + let ncred = Credential::new_password_only(cleartext); + let vcred = Value::new_credential("unix", ncred); + Ok(ModifyList::new_purge_and_set("unix_password", vcred)) + } + + pub(crate) fn verify_unix_credential( + &self, + _au: &mut AuditScope, + cleartext: &str, + ) -> Result, OperationError> { + // TODO #59: Is the cred locked? + // is the cred some or none? + match &self.cred { + Some(cred) => match &cred.password { + Some(pw) => { + if pw.verify(cleartext) { + Some(self.to_unixusertoken()).transpose() + } else { + // Failed to auth + Ok(None) + } + } + // We have a cred but it's not a password, that's weird + None => Err(OperationError::InvalidAccountState( + "non-password cred type?".to_string(), + )), + }, + // They don't have a unix cred, fail the auth. + None => Ok(None), + } + } } #[derive(Debug, Clone)] @@ -150,41 +224,37 @@ macro_rules! try_from_group_e { }}; } -impl UnixGroup { - pub fn try_from_account_entry_red_ro( - au: &mut AuditScope, - value: &Entry, - qs: &QueryServerReadTransaction, - ) -> Result, OperationError> { +macro_rules! try_from_account_group_e { + ($au:expr, $value:expr, $qs:expr) => {{ // First synthesise the self-group from the account. // We have already checked these, but paranoia is better than // complacency. - if !value.attribute_value_pres("class", &PVCLASS_ACCOUNT) { + if !$value.attribute_value_pres("class", &PVCLASS_ACCOUNT) { return Err(OperationError::InvalidAccountState( "Missing class: account".to_string(), )); } - if !value.attribute_value_pres("class", &PVCLASS_POSIXACCOUNT) { + if !$value.attribute_value_pres("class", &PVCLASS_POSIXACCOUNT) { return Err(OperationError::InvalidAccountState( "Missing class: posixaccount".to_string(), )); } - let name = value.get_ava_single_string("name").ok_or_else(|| { + let name = $value.get_ava_single_string("name").ok_or_else(|| { OperationError::InvalidAccountState("Missing attribute: name".to_string()) })?; - let spn = value + let spn = $value .get_ava_single("spn") .map(|v| v.to_proto_string_clone()) .ok_or_else(|| { OperationError::InvalidAccountState("Missing attribute: spn".to_string()) })?; - let uuid = *value.get_uuid(); + let uuid = *$value.get_uuid(); - let gidnumber = value.get_ava_single_uint32("gidnumber").ok_or_else(|| { + let gidnumber = $value.get_ava_single_uint32("gidnumber").ok_or_else(|| { OperationError::InvalidAccountState("Missing attribute: gidnumber".to_string()) })?; @@ -196,7 +266,7 @@ impl UnixGroup { uuid, }; - match value.get_ava_reference_uuid("memberof") { + match $value.get_ava_reference_uuid("memberof") { Some(l) => { let f = filter!(f_and!([ f_eq("class", PartialValue::new_class("posixgroup")), @@ -207,7 +277,7 @@ impl UnixGroup { .collect() ) ])); - let ges: Vec<_> = try_audit!(au, qs.internal_search(au, f)); + let ges: Vec<_> = $qs.internal_search($au, f)?; let groups: Result, _> = iter::once(Ok(upg)) .chain(ges.into_iter().map(UnixGroup::try_from_entry)) .collect(); @@ -218,6 +288,32 @@ impl UnixGroup { Ok(vec![upg]) } } + }}; +} + +impl UnixGroup { + pub fn try_from_account_entry_rw( + au: &mut AuditScope, + value: &Entry, + qs: &QueryServerWriteTransaction, + ) -> Result, OperationError> { + try_from_account_group_e!(au, value, qs) + } + + pub fn try_from_account_entry_ro( + au: &mut AuditScope, + value: &Entry, + qs: &QueryServerReadTransaction, + ) -> Result, OperationError> { + try_from_account_group_e!(au, value, qs) + } + + pub fn try_from_account_entry_red_ro( + au: &mut AuditScope, + value: &Entry, + qs: &QueryServerReadTransaction, + ) -> Result, OperationError> { + try_from_account_group_e!(au, value, qs) } pub fn try_from_entry_reduced( diff --git a/kanidmd/src/lib/lib.rs b/kanidmd/src/lib/lib.rs index 1407f4de0..81d5527b6 100644 --- a/kanidmd/src/lib/lib.rs +++ b/kanidmd/src/lib/lib.rs @@ -18,9 +18,9 @@ mod utils; mod async_log; #[macro_use] mod audit; -mod be; +pub mod be; pub mod constants; -mod credential; +pub mod credential; mod entry; mod event; mod filter; diff --git a/kanidmd/src/lib/server.rs b/kanidmd/src/lib/server.rs index 0267e1aca..f9e572009 100644 --- a/kanidmd/src/lib/server.rs +++ b/kanidmd/src/lib/server.rs @@ -100,7 +100,6 @@ pub trait QueryServerTransaction { Ok(entries_filtered) } - fn search( &self, au: &mut AuditScope, @@ -1734,6 +1733,7 @@ impl<'a> QueryServerWriteTransaction<'a> { JSON_SCHEMA_ATTR_GIDNUMBER, JSON_SCHEMA_ATTR_BADLIST_PASSWORD, JSON_SCHEMA_ATTR_LOGINSHELL, + JSON_SCHEMA_ATTR_UNIX_PASSWORD, JSON_SCHEMA_CLASS_PERSON, JSON_SCHEMA_CLASS_GROUP, JSON_SCHEMA_CLASS_ACCOUNT,