From 1a43ce5ac7033c148f64a033f2b1d335e95e11d5 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Fri, 8 Mar 2024 08:17:03 +0100 Subject: [PATCH] WIP refactor --- Cargo.lock | 1761 ++--------------- Cargo.toml | 41 +- aero-bayou/Cargo.toml | 17 + src/bayou.rs => aero-bayou/src/lib.rs | 9 +- {src => aero-bayou/src}/timestamp.rs | 3 +- {src => aero-collections}/mail/incoming.rs | 0 {src => aero-collections}/mail/mailbox.rs | 0 {src => aero-collections}/mail/mod.rs | 0 {src => aero-collections}/mail/namespace.rs | 0 {src => aero-collections}/mail/query.rs | 0 {src => aero-collections}/mail/snapshot.rs | 0 {src => aero-collections}/mail/uidindex.rs | 0 .../mail/unique_ident.rs | 0 {src => aero-collections}/user.rs | 0 aero-dav/.gitignore | 1 + aero-dav/Cargo.toml | 14 + {fuzz => aero-dav/fuzz}/.gitignore | 0 {fuzz => aero-dav/fuzz}/Cargo.lock | 0 {fuzz => aero-dav/fuzz}/Cargo.toml | 9 +- aero-dav/fuzz/dav.dict | 126 ++ aero-dav/fuzz/fuzz_targets/dav.rs | 196 ++ {src/dav => aero-dav/src}/acltypes.rs | 0 {src/dav => aero-dav/src}/caldecoder.rs | 0 {src/dav => aero-dav/src}/calencoder.rs | 4 +- {src/dav => aero-dav/src}/caltypes.rs | 25 +- {src/dav => aero-dav/src}/decoder.rs | 5 +- {src/dav => aero-dav/src}/encoder.rs | 9 +- {src/dav => aero-dav/src}/error.rs | 0 aero-dav/src/lib.rs | 25 + {src/dav => aero-dav/src}/realization.rs | 0 {src/dav => aero-dav/src}/types.rs | 1 - {src/dav => aero-dav/src}/versioningtypes.rs | 0 {src/dav => aero-dav/src}/xml.rs | 11 +- src/dav/mod.rs => aero-proto/dav.rs | 22 - {src => aero-proto}/imap/attributes.rs | 0 {src => aero-proto}/imap/capability.rs | 0 {src => aero-proto}/imap/command/anonymous.rs | 0 {src => aero-proto}/imap/command/anystate.rs | 0 .../imap/command/authenticated.rs | 0 {src => aero-proto}/imap/command/mod.rs | 0 {src => aero-proto}/imap/command/selected.rs | 0 {src => aero-proto}/imap/flags.rs | 0 {src => aero-proto}/imap/flow.rs | 0 {src => aero-proto}/imap/imf_view.rs | 0 {src => aero-proto}/imap/index.rs | 0 {src => aero-proto}/imap/mail_view.rs | 0 {src => aero-proto}/imap/mailbox_view.rs | 0 {src => aero-proto}/imap/mime_view.rs | 0 {src => aero-proto}/imap/mod.rs | 0 {src => aero-proto}/imap/request.rs | 0 {src => aero-proto}/imap/response.rs | 0 {src => aero-proto}/imap/search.rs | 0 {src => aero-proto}/imap/session.rs | 0 {src => aero-proto}/lmtp.rs | 0 aero-proto/sasl.rs | 140 ++ aero-sasl/Cargo.toml | 22 + aero-sasl/src/decode.rs | 243 +++ aero-sasl/src/encode.rs | 157 ++ aero-sasl/src/flow.rs | 201 ++ aero-sasl/src/lib.rs | 43 + aero-sasl/src/types.rs | 163 ++ aero-user/Cargo.toml | 30 + {src => aero-user/src}/config.rs | 0 {src => aero-user/src}/cryptoblob.rs | 0 aero-user/src/lib.rs | 9 + {src => aero-user/src}/login/demo_provider.rs | 0 {src => aero-user/src}/login/ldap_provider.rs | 3 +- {src => aero-user/src}/login/mod.rs | 2 +- .../src}/login/static_provider.rs | 7 +- {src => aero-user/src}/storage/garage.rs | 2 +- {src => aero-user/src}/storage/in_memory.rs | 4 +- {src => aero-user/src}/storage/mod.rs | 3 +- aerogramme/Cargo.toml | 12 + {src => aerogramme/src}/k2v_util.rs | 0 {src => aerogramme/src}/lib.rs | 0 {src => aerogramme/src}/main.rs | 0 {src => aerogramme/src}/server.rs | 0 flake.nix | 8 +- fuzz/fuzz_targets/dav.rs | 48 - src/auth.rs | 941 --------- 80 files changed, 1668 insertions(+), 2649 deletions(-) create mode 100644 aero-bayou/Cargo.toml rename src/bayou.rs => aero-bayou/src/lib.rs (99%) rename {src => aero-bayou/src}/timestamp.rs (99%) rename {src => aero-collections}/mail/incoming.rs (100%) rename {src => aero-collections}/mail/mailbox.rs (100%) rename {src => aero-collections}/mail/mod.rs (100%) rename {src => aero-collections}/mail/namespace.rs (100%) rename {src => aero-collections}/mail/query.rs (100%) rename {src => aero-collections}/mail/snapshot.rs (100%) rename {src => aero-collections}/mail/uidindex.rs (100%) rename {src => aero-collections}/mail/unique_ident.rs (100%) rename {src => aero-collections}/user.rs (100%) create mode 100644 aero-dav/.gitignore create mode 100644 aero-dav/Cargo.toml rename {fuzz => aero-dav/fuzz}/.gitignore (100%) rename {fuzz => aero-dav/fuzz}/Cargo.lock (100%) rename {fuzz => aero-dav/fuzz}/Cargo.toml (64%) create mode 100644 aero-dav/fuzz/dav.dict create mode 100644 aero-dav/fuzz/fuzz_targets/dav.rs rename {src/dav => aero-dav/src}/acltypes.rs (100%) rename {src/dav => aero-dav/src}/caldecoder.rs (100%) rename {src/dav => aero-dav/src}/calencoder.rs (99%) rename {src/dav => aero-dav/src}/caltypes.rs (99%) rename {src/dav => aero-dav/src}/decoder.rs (99%) rename {src/dav => aero-dav/src}/encoder.rs (99%) rename {src/dav => aero-dav/src}/error.rs (100%) create mode 100644 aero-dav/src/lib.rs rename {src/dav => aero-dav/src}/realization.rs (100%) rename {src/dav => aero-dav/src}/types.rs (99%) rename {src/dav => aero-dav/src}/versioningtypes.rs (100%) rename {src/dav => aero-dav/src}/xml.rs (96%) rename src/dav/mod.rs => aero-proto/dav.rs (95%) rename {src => aero-proto}/imap/attributes.rs (100%) rename {src => aero-proto}/imap/capability.rs (100%) rename {src => aero-proto}/imap/command/anonymous.rs (100%) rename {src => aero-proto}/imap/command/anystate.rs (100%) rename {src => aero-proto}/imap/command/authenticated.rs (100%) rename {src => aero-proto}/imap/command/mod.rs (100%) rename {src => aero-proto}/imap/command/selected.rs (100%) rename {src => aero-proto}/imap/flags.rs (100%) rename {src => aero-proto}/imap/flow.rs (100%) rename {src => aero-proto}/imap/imf_view.rs (100%) rename {src => aero-proto}/imap/index.rs (100%) rename {src => aero-proto}/imap/mail_view.rs (100%) rename {src => aero-proto}/imap/mailbox_view.rs (100%) rename {src => aero-proto}/imap/mime_view.rs (100%) rename {src => aero-proto}/imap/mod.rs (100%) rename {src => aero-proto}/imap/request.rs (100%) rename {src => aero-proto}/imap/response.rs (100%) rename {src => aero-proto}/imap/search.rs (100%) rename {src => aero-proto}/imap/session.rs (100%) rename {src => aero-proto}/lmtp.rs (100%) create mode 100644 aero-proto/sasl.rs create mode 100644 aero-sasl/Cargo.toml create mode 100644 aero-sasl/src/decode.rs create mode 100644 aero-sasl/src/encode.rs create mode 100644 aero-sasl/src/flow.rs create mode 100644 aero-sasl/src/lib.rs create mode 100644 aero-sasl/src/types.rs create mode 100644 aero-user/Cargo.toml rename {src => aero-user/src}/config.rs (100%) rename {src => aero-user/src}/cryptoblob.rs (100%) create mode 100644 aero-user/src/lib.rs rename {src => aero-user/src}/login/demo_provider.rs (100%) rename {src => aero-user/src}/login/ldap_provider.rs (99%) rename {src => aero-user/src}/login/mod.rs (100%) rename {src => aero-user/src}/login/static_provider.rs (99%) rename {src => aero-user/src}/storage/garage.rs (99%) rename {src => aero-user/src}/storage/in_memory.rs (99%) rename {src => aero-user/src}/storage/mod.rs (99%) create mode 100644 aerogramme/Cargo.toml rename {src => aerogramme/src}/k2v_util.rs (100%) rename {src => aerogramme/src}/lib.rs (100%) rename {src => aerogramme/src}/main.rs (100%) rename {src => aerogramme/src}/server.rs (100%) delete mode 100644 fuzz/fuzz_targets/dav.rs delete mode 100644 src/auth.rs diff --git a/Cargo.lock b/Cargo.lock index a4af312..20b9d95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "abnf-core" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182d1f071b906a9f59269c89af101515a5cbe58f723eb6717e7fe7445c0dea" -dependencies = [ - "nom 7.1.3", -] - [[package]] name = "addr2line" version = "0.21.0" @@ -27,7 +18,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "aerogramme" +name = "aero-bayou" +version = "0.3.0" +dependencies = [ + "aero-user", + "anyhow", + "log", + "rand", + "serde", + "tokio", +] + +[[package]] +name = "aero-dav" +version = "0.3.0" +dependencies = [ + "chrono", + "futures", + "http 1.1.0", + "quick-xml", + "tokio", +] + +[[package]] +name = "aero-sasl" +version = "0.3.0" +dependencies = [ + "anyhow", + "base64 0.21.7", + "futures", + "hex", + "nom 7.1.3", + "rand", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "aero-user" version = "0.3.0" dependencies = [ "anyhow", @@ -37,72 +66,31 @@ dependencies = [ "aws-sdk-s3", "aws-smithy-runtime", "aws-smithy-runtime-api", - "backtrace", "base64 0.21.7", - "chrono", - "clap", - "console-subscriber", - "duplexify", - "eml-codec", - "futures", - "hex", - "http 1.0.0", - "http-body-util", - "hyper 1.2.0", "hyper-rustls 0.26.0", "hyper-util", - "im", - "imap-codec", - "imap-flow", - "itertools", "k2v-client", - "lazy_static", "ldap3", "log", - "nix", - "nom 7.1.3", - "quick-xml", "rand", "rmp-serde", - "rpassword", - "rustls 0.22.2", - "rustls-pemfile 2.0.0", "serde", - "smtp-message", - "smtp-server", "sodiumoxide", - "thiserror", "tokio", - "tokio-rustls 0.25.0", - "tokio-util", "toml", "tracing", - "tracing-subscriber", "zstd", ] [[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +name = "aerogramme-fuzz" +version = "0.0.0" dependencies = [ - "memchr", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", + "aero-dav", + "arbitrary", + "libfuzzer-sys", + "quick-xml", + "tokio", ] [[package]] @@ -112,10 +100,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] -name = "argon2" -version = "0.5.2" +name = "arbitrary" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ba4cac0a46bc1d2912652a751c47f2a9f3a7fe89bcae2275d418f5270402f9" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" dependencies = [ "base64ct", "blake2", @@ -123,12 +120,6 @@ dependencies = [ "password-hash", ] -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "asn1-rs" version = "0.3.1" @@ -168,230 +159,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" -dependencies = [ - "concurrent-queue", - "event-listener 4.0.3", - "event-listener-strategy", - "futures-core", - "pin-project-lite 0.2.13", -] - -[[package]] -name = "async-executor" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" -dependencies = [ - "async-lock 3.3.0", - "async-task", - "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 2.2.0", - "slab", -] - -[[package]] -name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "blocking", - "futures-lite 1.13.0", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.1.1", - "async-executor", - "async-io 2.3.0", - "async-lock 3.3.0", - "blocking", - "futures-lite 2.2.0", - "once_cell", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb41eb19024a91746eba0773aa5e16036045bbf45733766661099e182ea6a744" -dependencies = [ - "async-lock 3.3.0", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite 2.2.0", - "parking", - "polling 3.3.2", - "rustix 0.38.30", - "slab", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" -dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy", - "pin-project-lite 0.2.13", -] - -[[package]] -name = "async-net" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0434b1ed18ce1cf5769b8ac540e33f01fa9471058b5e89da9e06f3c882a8c12f" -dependencies = [ - "async-io 1.13.0", - "blocking", - "futures-lite 1.13.0", -] - -[[package]] -name = "async-process" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" -dependencies = [ - "async-io 1.13.0", - "async-lock 2.8.0", - "async-signal", - "blocking", - "cfg-if", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.30", - "windows-sys 0.48.0", -] - -[[package]] -name = "async-signal" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" -dependencies = [ - "async-io 2.3.0", - "async-lock 2.8.0", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix 0.38.30", - "signal-hook-registry", - "slab", - "windows-sys 0.48.0", -] - -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io 1.13.0", - "async-lock 2.8.0", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite 1.13.0", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite 0.2.13", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite 0.2.13", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "async-task" -version = "4.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" - [[package]] name = "async-trait" version = "0.1.77" @@ -403,57 +170,6 @@ dependencies = [ "syn 2.0.48", ] -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "auto_enums" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0dfe45d75158751e195799f47ea02e81f570aa24bc5ef999cdd9e888c4b5c3" -dependencies = [ - "auto_enums_core", - "auto_enums_derive", -] - -[[package]] -name = "auto_enums_core" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da47c46001293a2c4b744d731958be22cff408a2ab76e2279328f9713b1267b4" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "auto_enums_derive" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41aed1da83ecdc799503b7cb94da1b45a34d72b49caf40a61d9cf5b88ec07cfd" -dependencies = [ - "autocfg", - "derive_utils", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -462,9 +178,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aws-config" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3182c19847238b50b62ae0383a6dbfc14514e552eb5e307e1ea83ccf5840b8a6" +checksum = "0b96342ea8948ab9bef3e6234ea97fc32e2d8a88d8fb6a084e52267317f94b6b" dependencies = [ "aws-credential-types", "aws-runtime", @@ -479,9 +195,9 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.0.1", + "fastrand", "hex", - "http 0.2.11", + "http 0.2.12", "hyper 0.14.28", "ring 0.17.7", "time", @@ -492,9 +208,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5635d8707f265c773282a22abe1ecd4fbe96a8eb2f0f14c0796f8016f11a41a" +checksum = "273fa47dafc9ef14c2c074ddddbea4561ff01b7f68d5091c0e9737ced605c01d" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -504,9 +220,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f82b9ae2adfd9d6582440d0eeb394c07f74d21b4c0cc72bdb73735c9e1a9c0e" +checksum = "6e38bab716c8bf07da24be07ecc02e0f5656ce8f30a891322ecdcb202f943b85" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -517,20 +233,20 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.0.1", - "http 0.2.11", + "fastrand", + "http 0.2.12", "http-body 0.4.6", "percent-encoding", - "pin-project-lite 0.2.13", + "pin-project-lite", "tracing", "uuid", ] [[package]] name = "aws-sdk-config" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb71960e3e197c3f512f3bf0f47f444acd708db59733416107ec2ff161ff5c4" +checksum = "07979fd68679736ba306d6ea2a4dc2fd835ac4d454942c5d8920ef83ed2f979f" dependencies = [ "aws-credential-types", "aws-runtime", @@ -542,7 +258,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "http 0.2.11", + "http 0.2.12", "once_cell", "regex-lite", "tracing", @@ -550,9 +266,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5076637347e7d0218e61facae853110682ae58efabd2f4e2a9e530c203d5fa7b" +checksum = "93d35d39379445970fc3e4ddf7559fff2c32935ce0b279f9cb27080d6b7c6d94" dependencies = [ "aws-credential-types", "aws-runtime", @@ -568,7 +284,7 @@ dependencies = [ "aws-smithy-xml", "aws-types", "bytes", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "once_cell", "percent-encoding", @@ -579,9 +295,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca7e8097448832fcd22faf6bb227e97d76b40e354509d1307653a885811c7151" +checksum = "d84bd3925a17c9adbf6ec65d52104a44a09629d8f70290542beeee69a95aee7f" dependencies = [ "aws-credential-types", "aws-runtime", @@ -593,7 +309,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "http 0.2.11", + "http 0.2.12", "once_cell", "regex-lite", "tracing", @@ -601,9 +317,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75073590e23d63044606771afae309fada8eb10ded54a1ce4598347221d3fef" +checksum = "2c2dae39e997f58bc4d6292e6244b26ba630c01ab671b6f9f44309de3eb80ab8" dependencies = [ "aws-credential-types", "aws-runtime", @@ -615,7 +331,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", - "http 0.2.11", + "http 0.2.12", "once_cell", "regex-lite", "tracing", @@ -623,9 +339,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650e4aaae41547151dea4d8142f7ffcc8ab8ba76d5dccc8933936ef2102c3356" +checksum = "17fd9a53869fee17cea77e352084e1aa71e2c5e323d974c13a9c2bcfd9544c7f" dependencies = [ "aws-credential-types", "aws-runtime", @@ -638,7 +354,7 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", - "http 0.2.11", + "http 0.2.12", "once_cell", "regex-lite", "tracing", @@ -646,9 +362,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "404c64a104188ac70dd1684718765cb5559795458e446480e41984e68e57d888" +checksum = "8ada00a4645d7d89f296fe0ddbc3fe3554f03035937c849a05d37ddffc1f29a1" dependencies = [ "aws-credential-types", "aws-smithy-eventstream", @@ -660,8 +376,8 @@ dependencies = [ "form_urlencoded", "hex", "hmac", - "http 0.2.11", - "http 1.0.0", + "http 0.2.12", + "http 1.1.0", "once_cell", "p256", "percent-encoding", @@ -680,7 +396,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcf7f09a27286d84315dfb9346208abb3b0973a692454ae6d0bc8d803fcce3b4" dependencies = [ "futures-util", - "pin-project-lite 0.2.13", + "pin-project-lite", "tokio", ] @@ -696,10 +412,10 @@ dependencies = [ "crc32c", "crc32fast", "hex", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "md-5", - "pin-project-lite 0.2.13", + "pin-project-lite", "sha1", "sha2", "tracing", @@ -728,11 +444,11 @@ dependencies = [ "bytes", "bytes-utils", "futures-core", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "once_cell", "percent-encoding", - "pin-project-lite 0.2.13", + "pin-project-lite", "pin-utils", "tracing", ] @@ -767,14 +483,14 @@ dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", "bytes", - "fastrand 2.0.1", + "fastrand", "h2 0.3.24", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "hyper 0.14.28", "hyper-rustls 0.24.2", "once_cell", - "pin-project-lite 0.2.13", + "pin-project-lite", "pin-utils", "rustls 0.21.10", "tokio", @@ -790,9 +506,9 @@ dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", - "http 0.2.11", - "http 1.0.0", - "pin-project-lite 0.2.13", + "http 0.2.12", + "http 1.1.0", + "pin-project-lite", "tokio", "tracing", "zeroize", @@ -808,11 +524,11 @@ dependencies = [ "bytes", "bytes-utils", "futures-core", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "itoa", "num-integer", - "pin-project-lite 0.2.13", + "pin-project-lite", "pin-utils", "ryu", "serde", @@ -832,64 +548,19 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fbb5d48aae496f628e7aa2e41991dd4074f606d9e3ade1ce1059f293d40f9a2" +checksum = "d07c63521aa1ea9a9f92a701f1a08ce3fd20b46c6efc0d5c8947c1fd879e3df1" dependencies = [ "aws-credential-types", "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "http 0.2.11", + "http 0.2.12", "rustc_version", "tracing", ] -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite 0.2.13", - "rustversion", - "serde", - "sync_wrapper", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 0.2.11", - "http-body 0.4.6", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - [[package]] name = "backtrace" version = "0.3.69" @@ -945,33 +616,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" - -[[package]] -name = "bitmaps" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" -dependencies = [ - "typenum", -] - -[[package]] -name = "bitvec" -version = "0.19.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "blake2" version = "0.10.6" @@ -990,42 +634,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "blocking" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" -dependencies = [ - "async-channel 2.1.1", - "async-lock 3.3.0", - "async-task", - "fastrand 2.0.1", - "futures-io", - "futures-lite 2.2.0", - "piper", - "tracing", -] - -[[package]] -name = "bounded-static" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2325bd33fa7e3018e7e37f5b0591ba009124963b5a3f8b7cae6d0a8c1028ed4" -dependencies = [ - "bounded-static-derive", -] - -[[package]] -name = "bounded-static-derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f10dd247355bf631d98d2753d87ae62c84c8dcb996ad9b24a4168e0aec29bd6b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "bumpalo" version = "3.14.0" @@ -1072,101 +680,11 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", "num-traits", - "wasm-bindgen", - "windows-targets 0.48.5", -] - -[[package]] -name = "clap" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "atty", - "bitflags 1.3.2", - "clap_derive", - "clap_lex", - "indexmap 1.9.3", - "once_cell", - "strsim", - "termcolor", - "textwrap", -] - -[[package]] -name = "clap_derive" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "concurrent-queue" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console-api" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787" -dependencies = [ - "futures-core", - "prost", - "prost-types", - "tonic", - "tracing-core", -] - -[[package]] -name = "console-subscriber" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7481d4c57092cd1c19dd541b92bdce883de840df30aa5d03fd48a3935c01842e" -dependencies = [ - "console-api", - "crossbeam-channel", - "crossbeam-utils", - "futures-task", - "hdrhistogram", - "humantime", - "prost-types", - "serde", - "serde_json", - "thread_local", - "tokio", - "tokio-stream", - "tonic", - "tracing", - "tracing-core", - "tracing-subscriber", ] [[package]] @@ -1211,28 +729,13 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - [[package]] name = "crypto-bigint" version = "0.4.9" @@ -1305,14 +808,14 @@ dependencies = [ ] [[package]] -name = "derive_utils" -version = "0.11.2" +name = "derive_arbitrary" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532b4c15dccee12c7044f1fcad956e98410860b22231e44a3b827464797ca7bf" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -1337,16 +840,6 @@ dependencies = [ "syn 2.0.48", ] -[[package]] -name = "duplexify" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1cc346cd6db38ceab2d33f59b26024c3ddb8e75f047c6cafbcbc016ea8065d5" -dependencies = [ - "async-std", - "pin-project-lite 0.1.12", -] - [[package]] name = "ecdsa" version = "0.14.8" @@ -1370,9 +863,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "elliptic-curve" @@ -1394,90 +887,12 @@ dependencies = [ "zeroize", ] -[[package]] -name = "eml-codec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4499124d87abce26a57ef96ece800fa8babc38fbedd81c607c340ae83d46d2e" -dependencies = [ - "base64 0.21.7", - "chrono", - "encoding_rs", - "nom 7.1.3", -] - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite 0.2.13", -] - -[[package]] -name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite 0.2.13", -] - -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite 0.2.13", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.0.1" @@ -1494,16 +909,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "flate2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1519,12 +924,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - [[package]] name = "futures" version = "0.3.30" @@ -1573,34 +972,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite 0.2.13", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" -dependencies = [ - "fastrand 2.0.1", - "futures-core", - "futures-io", - "parking", - "pin-project-lite 0.2.13", -] - [[package]] name = "futures-macro" version = "0.3.30" @@ -1637,7 +1008,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.13", + "pin-project-lite", "pin-utils", "slab", ] @@ -1669,18 +1040,6 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "group" version = "0.12.1" @@ -1703,8 +1062,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 0.2.11", - "indexmap 2.1.0", + "http 0.2.12", + "indexmap", "slab", "tokio", "tokio-util", @@ -1722,54 +1081,20 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 1.0.0", - "indexmap 2.1.0", + "http 1.1.0", + "indexmap", "slab", "tokio", "tokio-util", "tracing", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -[[package]] -name = "hdrhistogram" -version = "7.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" -dependencies = [ - "base64 0.21.7", - "byteorder", - "flate2", - "nom 7.1.3", - "num-traits", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.4" @@ -1793,9 +1118,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1804,9 +1129,9 @@ dependencies = [ [[package]] name = "http" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -1820,8 +1145,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http 0.2.11", - "pin-project-lite 0.2.13", + "http 0.2.12", + "pin-project-lite", ] [[package]] @@ -1831,7 +1156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http 1.0.0", + "http 1.1.0", ] [[package]] @@ -1842,9 +1167,9 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", - "pin-project-lite 0.2.13", + "pin-project-lite", ] [[package]] @@ -1859,12 +1184,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hyper" version = "0.14.28" @@ -1876,13 +1195,13 @@ dependencies = [ "futures-core", "futures-util", "h2 0.3.24", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.13", - "socket2 0.5.5", + "pin-project-lite", + "socket2", "tokio", "tower-service", "tracing", @@ -1899,12 +1218,12 @@ dependencies = [ "futures-channel", "futures-util", "h2 0.4.2", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.13", + "pin-project-lite", "smallvec", "tokio", "want", @@ -1917,7 +1236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http 0.2.11", + "http 0.2.12", "hyper 0.14.28", "log", "rustls 0.21.10", @@ -1933,7 +1252,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http 1.0.0", + "http 1.1.0", "hyper 1.2.0", "hyper-util", "log", @@ -1945,18 +1264,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper 0.14.28", - "pin-project-lite 0.2.13", - "tokio", - "tokio-io-timeout", -] - [[package]] name = "hyper-util" version = "0.1.3" @@ -1966,51 +1273,17 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "hyper 1.2.0", - "pin-project-lite 0.2.13", - "socket2 0.5.5", + "pin-project-lite", + "socket2", "tokio", "tower", "tower-service", "tracing", ] -[[package]] -name = "iana-time-zone" -version = "0.1.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.5.0" @@ -2021,107 +1294,14 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "im" -version = "15.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" -dependencies = [ - "bitmaps", - "rand_core", - "rand_xoshiro", - "sized-chunks", - "typenum", - "version_check", -] - -[[package]] -name = "imap-codec" -version = "2.0.0" -source = "git+https://github.com/superboum/imap-codec?branch=custom/aerogramme#d8a5afc03fb771232e94c73af6a05e79dc80bbed" -dependencies = [ - "abnf-core", - "base64 0.21.7", - "bounded-static", - "chrono", - "imap-types", - "log", - "nom 7.1.3", - "thiserror", -] - -[[package]] -name = "imap-flow" -version = "0.1.0" -source = "git+https://github.com/duesee/imap-flow.git?branch=main#68c1da5d1c56dbe543d9736de9683259d1d28191" -dependencies = [ - "bounded-static", - "bytes", - "imap-codec", - "imap-types", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "imap-types" -version = "2.0.0" -source = "git+https://github.com/superboum/imap-codec?branch=custom/aerogramme#d8a5afc03fb771232e94c73af6a05e79dc80bbed" -dependencies = [ - "base64 0.21.7", - "bounded-static", - "chrono", - "thiserror", -] - [[package]] name = "indexmap" -version = "1.9.3" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", - "hashbrown 0.14.3", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.4", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", + "hashbrown", ] [[package]] @@ -2157,7 +1337,7 @@ dependencies = [ "aws-sigv4", "base64 0.21.7", "hex", - "http 1.0.0", + "http 1.1.0", "http-body-util", "hyper 1.2.0", "hyper-rustls 0.26.0", @@ -2171,15 +1351,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -2224,25 +1395,23 @@ dependencies = [ "x509-parser", ] -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags 1.3.2", - "cfg-if", - "ryu", - "static_assertions", -] - [[package]] name = "libc" version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libsodium-sys" version = "0.2.7" @@ -2255,47 +1424,11 @@ dependencies = [ "walkdir", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -dependencies = [ - "value-bag", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "md-5" @@ -2313,12 +1446,6 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2345,36 +1472,12 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.4.2", - "cfg-if", - "libc", -] - [[package]] name = "nom" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff" -[[package]] -name = "nom" -version = "6.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" -dependencies = [ - "bitvec", - "funty", - "lexical-core", - "memchr", - "version_check", -] - [[package]] name = "nom" version = "7.1.3" @@ -2385,16 +1488,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num-bigint" version = "0.4.4" @@ -2431,7 +1524,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.4", + "hermit-abi", "libc", ] @@ -2465,24 +1558,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" - [[package]] name = "outref" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "p256" version = "0.11.1" @@ -2494,12 +1575,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - [[package]] name = "password-hash" version = "0.5.0" @@ -2525,30 +1600,24 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", "syn 2.0.48", ] -[[package]] -name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - [[package]] name = "pin-project-lite" version = "0.2.13" @@ -2561,17 +1630,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "piper" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" -dependencies = [ - "atomic-waker", - "fastrand 2.0.1", - "futures-io", -] - [[package]] name = "pkcs8" version = "0.9.0" @@ -2588,36 +1646,6 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite 0.2.13", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" -dependencies = [ - "cfg-if", - "concurrent-queue", - "pin-project-lite 0.2.13", - "rustix 0.38.30", - "tracing", - "windows-sys 0.52.0", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -2630,30 +1658,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.76" @@ -2663,38 +1667,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "prost" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "prost-types" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" -dependencies = [ - "prost", -] - [[package]] name = "quick-xml" version = "0.31.0" @@ -2714,12 +1686,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" - [[package]] name = "rand" version = "0.8.5" @@ -2750,65 +1716,12 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_xoshiro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" -dependencies = [ - "rand_core", -] - -[[package]] -name = "regex" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.2", -] - [[package]] name = "regex-lite" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e" -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - [[package]] name = "rfc6979" version = "0.3.1" @@ -2871,27 +1784,6 @@ dependencies = [ "serde", ] -[[package]] -name = "rpassword" -version = "7.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" -dependencies = [ - "libc", - "rtoolbox", - "windows-sys 0.48.0", -] - -[[package]] -name = "rtoolbox" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -2916,33 +1808,6 @@ dependencies = [ "nom 7.1.3", ] -[[package]] -name = "rustix" -version = "0.37.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" -dependencies = [ - "bitflags 2.4.2", - "errno", - "libc", - "linux-raw-sys 0.4.13", - "windows-sys 0.52.0", -] - [[package]] name = "rustls" version = "0.20.9" @@ -2976,7 +1841,7 @@ dependencies = [ "log", "ring 0.17.7", "rustls-pki-types", - "rustls-webpki 0.102.1", + "rustls-webpki 0.102.2", "subtle", "zeroize", ] @@ -3000,7 +1865,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile 2.0.0", + "rustls-pemfile 2.1.1", "rustls-pki-types", "schannel", "security-framework", @@ -3017,9 +1882,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" +checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" dependencies = [ "base64 0.21.7", "rustls-pki-types", @@ -3027,9 +1892,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" +checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" [[package]] name = "rustls-webpki" @@ -3043,26 +1908,20 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.1" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring 0.17.7", "rustls-pki-types", "untrusted 0.9.0", ] -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -3112,7 +1971,7 @@ version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -3131,9 +1990,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" @@ -3157,9 +2016,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -3188,15 +2047,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -3216,16 +2066,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "sized-chunks" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" -dependencies = [ - "bitmaps", - "typenum", -] - [[package]] name = "slab" version = "0.4.9" @@ -3241,71 +2081,6 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" -[[package]] -name = "smol" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" -dependencies = [ - "async-channel 1.9.0", - "async-executor", - "async-fs", - "async-io 1.13.0", - "async-lock 2.8.0", - "async-net", - "async-process", - "blocking", - "futures-lite 1.13.0", -] - -[[package]] -name = "smtp-message" -version = "0.1.0" -source = "git+http://github.com/Alexis211/kannader?branch=feature/lmtp#0560e7c46af752344a3095add5f84b02400b1111" -dependencies = [ - "auto_enums", - "futures", - "idna 0.2.3", - "lazy_static", - "nom 6.1.2", - "pin-project", - "regex-automata 0.1.10", - "serde", -] - -[[package]] -name = "smtp-server" -version = "0.1.0" -source = "git+http://github.com/Alexis211/kannader?branch=feature/lmtp#0560e7c46af752344a3095add5f84b02400b1111" -dependencies = [ - "async-trait", - "chrono", - "duplexify", - "futures", - "smol", - "smtp-message", - "smtp-server-types", -] - -[[package]] -name = "smtp-server-types" -version = "0.1.0" -source = "git+http://github.com/Alexis211/kannader?branch=feature/lmtp#0560e7c46af752344a3095add5f84b02400b1111" -dependencies = [ - "serde", - "smtp-message", -] - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.5" @@ -3350,18 +2125,6 @@ dependencies = [ "der", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "subtle" version = "2.5.0" @@ -3390,12 +2153,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "synstructure" version = "0.12.6" @@ -3408,27 +2165,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.56" @@ -3449,16 +2185,6 @@ dependencies = [ "syn 2.0.48", ] -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - [[package]] name = "time" version = "0.3.31" @@ -3514,24 +2240,13 @@ dependencies = [ "libc", "mio", "num_cpus", - "pin-project-lite 0.2.13", + "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", - "tracing", "windows-sys 0.48.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite 0.2.13", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.2.0" @@ -3582,7 +2297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", - "pin-project-lite 0.2.13", + "pin-project-lite", "tokio", ] @@ -3596,7 +2311,7 @@ dependencies = [ "futures-core", "futures-io", "futures-sink", - "pin-project-lite 0.2.13", + "pin-project-lite", "tokio", "tracing", ] @@ -3610,33 +2325,6 @@ dependencies = [ "serde", ] -[[package]] -name = "tonic" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.21.7", - "bytes", - "h2 0.3.24", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost", - "tokio", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "tower" version = "0.4.13" @@ -3645,13 +2333,9 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "indexmap 1.9.3", "pin-project", - "pin-project-lite 0.2.13", - "rand", - "slab", + "pin-project-lite", "tokio", - "tokio-util", "tower-layer", "tower-service", "tracing", @@ -3676,7 +2360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", - "pin-project-lite 0.2.13", + "pin-project-lite", "tracing-attributes", "tracing-core", ] @@ -3699,36 +2383,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", ] [[package]] @@ -3789,7 +2443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -3805,18 +2459,6 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "value-bag" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cdbaf5e132e593e9fc1de6a15bbec912395b11fb9719e061cf64f804524c503" - [[package]] name = "version_check" version = "0.9.4" @@ -3829,12 +2471,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" -[[package]] -name = "waker-fn" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" - [[package]] name = "walkdir" version = "2.4.0" @@ -3885,18 +2521,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.90" @@ -3977,15 +2601,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.0", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -4118,12 +2733,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - [[package]] name = "x509-parser" version = "0.13.2" @@ -4182,3 +2791,13 @@ dependencies = [ "cc", "libc", ] + +[[patch.unused]] +name = "imap-codec" +version = "2.0.0" +source = "git+https://github.com/superboum/imap-codec?branch=custom/aerogramme#d8a5afc03fb771232e94c73af6a05e79dc80bbed" + +[[patch.unused]] +name = "imap-types" +version = "2.0.0" +source = "git+https://github.com/superboum/imap-codec?branch=custom/aerogramme#d8a5afc03fb771232e94c73af6a05e79dc80bbed" diff --git a/Cargo.toml b/Cargo.toml index 543b463..56d5cf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,28 @@ -[package] -name = "aerogramme" -version = "0.3.0" -authors = ["Alex Auvolat ", "Quentin Dufour "] -edition = "2021" -license = "EUPL-1.2" -description = "A robust email server" +[workspace] +resolver = "2" +members = [ + "aero-user", + "aero-bayou", + "aero-sasl", + "aero-dav", + "aero-dav/fuzz", +# "aero-collections", +# "aero-proto", +# "aerogramme", +] -[lib] -name = "aerogramme" -path = "src/lib.rs" +default-members = ["aerogramme"] + +[workspace.dependencies] +# internal crates +aero-user = { version = "0.3.0", path = "aero-user" } +aero-bayou = { version = "0.3.0", path = "aero-bayou" } +aero-sasl = { version = "0.3.0", path = "aero-sasl" } +aero-dav = { version = "0.3.0", path = "aero-dav" } +#aero-collections = { version = "0.3.0", path = "aero-collections" } +#aero-proto = { version = "0.3.0", path = "aero-proto" } +#aerogramme = { version = "0.3.0", path = "aerogramme" } -[dependencies] # async runtime tokio = { version = "1.18", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros", "sync", "signal", "fs"] } tokio-util = { version = "0.7", features = [ "compat" ] } @@ -80,13 +92,6 @@ aws-sdk-s3 = "1" aws-smithy-runtime = "1" aws-smithy-runtime-api = "1" -[dev-dependencies] - [patch.crates-io] imap-types = { git = "https://github.com/superboum/imap-codec", branch = "custom/aerogramme" } imap-codec = { git = "https://github.com/superboum/imap-codec", branch = "custom/aerogramme" } - -[[test]] -name = "behavior" -path = "tests/behavior.rs" -harness = false diff --git a/aero-bayou/Cargo.toml b/aero-bayou/Cargo.toml new file mode 100644 index 0000000..d271f4a --- /dev/null +++ b/aero-bayou/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "aero-bayou" +version = "0.3.0" +authors = ["Alex Auvolat ", "Quentin Dufour "] +edition = "2021" +license = "EUPL-1.2" +description = "A simplified version of Bayou by Terry et al. (ACM SIGOPS 1995)" + +[dependencies] +aero-user.workspace = true + +anyhow.workspace = true +log.workspace = true +rand.workspace = true +serde.workspace = true +tokio.workspace = true + diff --git a/src/bayou.rs b/aero-bayou/src/lib.rs similarity index 99% rename from src/bayou.rs rename to aero-bayou/src/lib.rs index 9faff5a..7756964 100644 --- a/src/bayou.rs +++ b/aero-bayou/src/lib.rs @@ -1,3 +1,5 @@ +mod timestamp + use std::sync::{Arc, Weak}; use std::time::{Duration, Instant}; @@ -7,9 +9,10 @@ use rand::prelude::*; use serde::{Deserialize, Serialize}; use tokio::sync::{watch, Notify}; -use crate::cryptoblob::*; -use crate::login::Credentials; -use crate::storage; +use aero_foundations::cryptoblob::*; +use aero_foundations::login::Credentials; +use aero_foundations::storage; + use crate::timestamp::*; const KEEP_STATE_EVERY: usize = 64; diff --git a/src/timestamp.rs b/aero-bayou/src/timestamp.rs similarity index 99% rename from src/timestamp.rs rename to aero-bayou/src/timestamp.rs index 76cb74b..4aa5399 100644 --- a/src/timestamp.rs +++ b/aero-bayou/src/timestamp.rs @@ -1,7 +1,8 @@ -use rand::prelude::*; use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; +use rand::prelude::*; + /// Returns milliseconds since UNIX Epoch pub fn now_msec() -> u64 { SystemTime::now() diff --git a/src/mail/incoming.rs b/aero-collections/mail/incoming.rs similarity index 100% rename from src/mail/incoming.rs rename to aero-collections/mail/incoming.rs diff --git a/src/mail/mailbox.rs b/aero-collections/mail/mailbox.rs similarity index 100% rename from src/mail/mailbox.rs rename to aero-collections/mail/mailbox.rs diff --git a/src/mail/mod.rs b/aero-collections/mail/mod.rs similarity index 100% rename from src/mail/mod.rs rename to aero-collections/mail/mod.rs diff --git a/src/mail/namespace.rs b/aero-collections/mail/namespace.rs similarity index 100% rename from src/mail/namespace.rs rename to aero-collections/mail/namespace.rs diff --git a/src/mail/query.rs b/aero-collections/mail/query.rs similarity index 100% rename from src/mail/query.rs rename to aero-collections/mail/query.rs diff --git a/src/mail/snapshot.rs b/aero-collections/mail/snapshot.rs similarity index 100% rename from src/mail/snapshot.rs rename to aero-collections/mail/snapshot.rs diff --git a/src/mail/uidindex.rs b/aero-collections/mail/uidindex.rs similarity index 100% rename from src/mail/uidindex.rs rename to aero-collections/mail/uidindex.rs diff --git a/src/mail/unique_ident.rs b/aero-collections/mail/unique_ident.rs similarity index 100% rename from src/mail/unique_ident.rs rename to aero-collections/mail/unique_ident.rs diff --git a/src/user.rs b/aero-collections/user.rs similarity index 100% rename from src/user.rs rename to aero-collections/user.rs diff --git a/aero-dav/.gitignore b/aero-dav/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/aero-dav/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/aero-dav/Cargo.toml b/aero-dav/Cargo.toml new file mode 100644 index 0000000..92929b1 --- /dev/null +++ b/aero-dav/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "aero-dav" +version = "0.3.0" +authors = ["Alex Auvolat ", "Quentin Dufour "] +edition = "2021" +license = "EUPL-1.2" +description = "A partial and standalone implementation of the WebDAV protocol and its extensions (eg. CalDAV or CardDAV)" + +[dependencies] +quick-xml.workspace = true +http.workspace = true +chrono.workspace = true +tokio.workspace = true +futures.workspace = true diff --git a/fuzz/.gitignore b/aero-dav/fuzz/.gitignore similarity index 100% rename from fuzz/.gitignore rename to aero-dav/fuzz/.gitignore diff --git a/fuzz/Cargo.lock b/aero-dav/fuzz/Cargo.lock similarity index 100% rename from fuzz/Cargo.lock rename to aero-dav/fuzz/Cargo.lock diff --git a/fuzz/Cargo.toml b/aero-dav/fuzz/Cargo.toml similarity index 64% rename from fuzz/Cargo.toml rename to aero-dav/fuzz/Cargo.toml index 25c1f15..a450853 100644 --- a/fuzz/Cargo.toml +++ b/aero-dav/fuzz/Cargo.toml @@ -8,17 +8,14 @@ edition = "2021" cargo-fuzz = true [dependencies] -libfuzzer-sys = "0.4" +arbitrary = { version = "1", optional = true, features = ["derive"] } +libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] } tokio = { version = "1.18", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros", "sync", "signal", "fs"] } quick-xml = { version = "0.31", features = ["async-tokio"] } -[dependencies.aerogramme] +[dependencies.aero-dav] path = ".." -[patch.crates-io] -imap-types = { git = "https://github.com/superboum/imap-codec", branch = "custom/aerogramme" } -imap-codec = { git = "https://github.com/superboum/imap-codec", branch = "custom/aerogramme" } - [[bin]] name = "dav" path = "fuzz_targets/dav.rs" diff --git a/aero-dav/fuzz/dav.dict b/aero-dav/fuzz/dav.dict new file mode 100644 index 0000000..3ef5b69 --- /dev/null +++ b/aero-dav/fuzz/dav.dict @@ -0,0 +1,126 @@ +# +# AFL dictionary for XML +# ---------------------- +# +# Several basic syntax elements and attributes, modeled on libxml2. +# +# Created by Michal Zalewski +# + +attr_encoding=" encoding=\"1\"" +attr_generic=" a=\"1\"" +attr_href=" href=\"1\"" +attr_standalone=" standalone=\"no\"" +attr_version=" version=\"1\"" +attr_xml_base=" xml:base=\"1\"" +attr_xml_id=" xml:id=\"1\"" +attr_xml_lang=" xml:lang=\"1\"" +attr_xml_space=" xml:space=\"1\"" +attr_xmlns=" xmlns=\"1\"" + +entity_builtin="<" +entity_decimal="" +entity_external="&a;" +entity_hex="" + +string_any="ANY" +string_brackets="[]" +string_cdata="CDATA" +string_col_fallback=":fallback" +string_col_generic=":a" +string_col_include=":include" +string_dashes="--" +string_empty="EMPTY" +string_empty_dblquotes="\"\"" +string_empty_quotes="''" +string_entities="ENTITIES" +string_entity="ENTITY" +string_fixed="#FIXED" +string_id="ID" +string_idref="IDREF" +string_idrefs="IDREFS" +string_implied="#IMPLIED" +string_nmtoken="NMTOKEN" +string_nmtokens="NMTOKENS" +string_notation="NOTATION" +string_parentheses="()" +string_pcdata="#PCDATA" +string_percent="%a" +string_public="PUBLIC" +string_required="#REQUIRED" +string_schema=":schema" +string_system="SYSTEM" +string_ucs4="UCS-4" +string_utf16="UTF-16" +string_utf8="UTF-8" +string_xmlns="xmlns:" + +tag_attlist="" +tag_doctype="" +tag_open_close="" +tag_open_exclamation="" +tag_xml_q="" + +"0" +"1" +"activelock" +"allprop" +"cannot-modify-protected-property" +"collection" +"creationdate" +"DAV:" +"depth" +"displayname" +"error" +"exclusive" +"getcontentlanguage" +"getcontentlength" +"getcontenttype" +"getetag" +"getlastmodified" +"href" +"include" +"Infinite" +"infinity" +"location" +"lockdiscovery" +"lockentry" +"lockinfo" +"lockroot" +"lockscope" +"locktoken" +"lock-token-matches-request-uri" +"lock-token-submitted" +"locktype" +"multistatus" +"no-conflicting-lock" +"no-external-entities" +"owner" +"preserved-live-properties" +"prop" +"propertyupdate" +"propfind" +"propfind-finite-depth" +"propname" +"propstat" +"remove" +"resourcetype" +"response" +"responsedescription" +"set" +"shared" +"status" +"supportedlock" +"text/html" +"timeout" +"write" diff --git a/aero-dav/fuzz/fuzz_targets/dav.rs b/aero-dav/fuzz/fuzz_targets/dav.rs new file mode 100644 index 0000000..a3c6ece --- /dev/null +++ b/aero-dav/fuzz/fuzz_targets/dav.rs @@ -0,0 +1,196 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use libfuzzer_sys::arbitrary; +use libfuzzer_sys::arbitrary::Arbitrary; + +use aero_dav::{types, realization, xml}; +use quick_xml::reader::NsReader; +use tokio::runtime::Runtime; +use tokio::io::AsyncWriteExt; + +const tokens: [&str; 63] = [ +"0", +"1", +"activelock", +"allprop", +"encoding", +"utf-8", +"http://ns.example.com/boxschema/", +"HTTP/1.1 200 OK", +"1997-12-01T18:27:21-08:00", +"Mon, 12 Jan 1998 09:25:56 GMT", +"\"abcdef\"", +"cannot-modify-protected-property", +"collection", +"creationdate", +"DAV:", +"D", +"C", +"xmlns:D", +"depth", +"displayname", +"error", +"exclusive", +"getcontentlanguage", +"getcontentlength", +"getcontenttype", +"getetag", +"getlastmodified", +"href", +"include", +"Infinite", +"infinity", +"location", +"lockdiscovery", +"lockentry", +"lockinfo", +"lockroot", +"lockscope", +"locktoken", +"lock-token-matches-request-uri", +"lock-token-submitted", +"locktype", +"multistatus", +"no-conflicting-lock", +"no-external-entities", +"owner", +"preserved-live-properties", +"prop", +"propertyupdate", +"propfind", +"propfind-finite-depth", +"propname", +"propstat", +"remove", +"resourcetype", +"response", +"responsedescription", +"set", +"shared", +"status", +"supportedlock", +"text/html", +"timeout", +"write", +]; + +#[derive(Arbitrary)] +enum Token { + Known(usize), + //Unknown(String), +} +impl Token { + fn serialize(&self) -> String { + match self { + Self::Known(i) => tokens[i % tokens.len()].to_string(), + //Self::Unknown(v) => v.to_string(), + } + } +} + +#[derive(Arbitrary)] +struct Tag { + //prefix: Option, + name: Token, + attr: Option<(Token, Token)>, +} +impl Tag { + fn start(&self) -> String { + let mut acc = String::new(); + /*if let Some(p) = &self.prefix { + acc.push_str(p.serialize().as_str()); + acc.push_str(":"); + }*/ + acc.push_str("D:"); + acc.push_str(self.name.serialize().as_str()); + + if let Some((k,v)) = &self.attr { + acc.push_str(" "); + acc.push_str(k.serialize().as_str()); + acc.push_str("=\""); + acc.push_str(v.serialize().as_str()); + acc.push_str("\""); + } + acc + } + fn end(&self) -> String { + let mut acc = String::new(); + acc.push_str("D:"); + acc.push_str(self.name.serialize().as_str()); + acc + } +} + + +#[derive(Arbitrary)] +enum XmlNode { + Node(Tag, Vec), + Number(u64), + Text(Token), +} +impl std::fmt::Debug for XmlNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.serialize()) + } +} +impl XmlNode { + fn serialize(&self) -> String { + match self { + Self::Node(tag, children) => { + let stag = tag.start(); + match children.is_empty() { + true => format!("<{}/>", stag), + false => format!("<{}>{}", stag, children.iter().map(|v| v.serialize()).collect::(), tag.end()), + } + }, + Self::Number(v) => format!("{}", v), + Self::Text(v) => v.serialize(), + } + } +} + +async fn serialize(elem: &impl xml::QWrite) -> Vec { + let mut buffer = Vec::new(); + let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer); + let q = quick_xml::writer::Writer::new_with_indent(&mut tokio_buffer, b' ', 4); + let ns_to_apply = vec![ ("xmlns:D".into(), "DAV:".into()) ]; + let mut writer = xml::Writer { q, ns_to_apply }; + + elem.qwrite(&mut writer).await.expect("xml serialization"); + tokio_buffer.flush().await.expect("tokio buffer flush"); + + return buffer +} + +type Object = types::Multistatus>; + +fuzz_target!(|nodes: XmlNode| { + let gen = format!("{}", nodes.serialize()); + //println!("--------\n{}", gen); + let data = gen.as_bytes(); + + let rt = Runtime::new().expect("tokio runtime initialization"); + + rt.block_on(async { + // 1. Setup fuzzing by finding an input that seems correct, do not crash yet then. + let mut rdr = match xml::Reader::new(NsReader::from_reader(data)).await { + Err(_) => return, + Ok(r) => r, + }; + let reference = match rdr.find::().await { + Err(_) => return, + Ok(m) => m, + }; + + // 2. Re-serialize the input + let my_serialization = serialize(&reference).await; + + // 3. De-serialize my serialization + let mut rdr2 = xml::Reader::new(NsReader::from_reader(my_serialization.as_slice())).await.expect("XML Reader init"); + let comparison = rdr2.find::().await.expect("Deserialize again"); + + // 4. Both the first decoding and last decoding must be identical + assert_eq!(reference, comparison); + }) +}); diff --git a/src/dav/acltypes.rs b/aero-dav/src/acltypes.rs similarity index 100% rename from src/dav/acltypes.rs rename to aero-dav/src/acltypes.rs diff --git a/src/dav/caldecoder.rs b/aero-dav/src/caldecoder.rs similarity index 100% rename from src/dav/caldecoder.rs rename to aero-dav/src/caldecoder.rs diff --git a/src/dav/calencoder.rs b/aero-dav/src/calencoder.rs similarity index 99% rename from src/dav/calencoder.rs rename to aero-dav/src/calencoder.rs index 58b88c7..ff6eb24 100644 --- a/src/dav/calencoder.rs +++ b/aero-dav/src/calencoder.rs @@ -665,8 +665,8 @@ impl QWrite for TimeRange { #[cfg(test)] mod tests { use super::*; - use crate::dav::types as dav; - use crate::dav::realization::Calendar; + use crate::types as dav; + use crate::realization::Calendar; use tokio::io::AsyncWriteExt; use chrono::{Utc,TimeZone,DateTime}; diff --git a/src/dav/caltypes.rs b/aero-dav/src/caltypes.rs similarity index 99% rename from src/dav/caltypes.rs rename to aero-dav/src/caltypes.rs index befecef..9b9091e 100644 --- a/src/dav/caltypes.rs +++ b/aero-dav/src/caltypes.rs @@ -26,7 +26,9 @@ use super::xml; /// processing details can be found in the definition of the DAV:set /// instruction in Section 12.13.2 of [RFC2518]. /// +/// ```xmlschema /// +/// ``` #[derive(Debug, PartialEq)] pub struct MkCalendar(pub dav::Set); @@ -197,12 +199,15 @@ pub enum Property { /// sequence "]]>", which is the end delimiter for the CDATA section. /// /// Definition: - /// + /// + /// ```xmlschema /// /// PCDATA value: an iCalendar object with exactly one VTIMEZONE component. + /// ``` /// /// Example: /// + /// ```xmlschema /// BEGIN:VCALENDAR /// PRODID:-//Example Corp.//CalDAV Client//EN @@ -227,6 +232,7 @@ pub enum Property { /// END:VTIMEZONE /// END:VCALENDAR /// + /// ``` //@FIXME we might want to put a buffer here or an iCal parsed object CalendarTimezone(String), @@ -1123,12 +1129,15 @@ pub enum CalendarSelector { /// the targeted calendar component. /// /// Definition: +/// +/// ```xmlschema /// /// /// /// name value: a calendar object or calendar component /// type (e.g., VEVENT) +/// ``` #[derive(Debug, PartialEq)] pub struct CompFilter { pub name: Component, @@ -1187,12 +1196,14 @@ pub struct CompFilterMatch { /// /// Definition: /// -/// +/// ```xmlschema +/// /// -/// -/// name value: a calendar property name (e.g., ATTENDEE) +/// +/// name value: a calendar property name (e.g., ATTENDEE) +/// ``` #[derive(Debug, PartialEq)] pub struct PropFilter { pub name: Component, @@ -1278,10 +1289,12 @@ pub struct TextMatch { /// /// Definition: /// +/// ```xmlschema /// /// /// /// name value: a property parameter name (e.g., PARTSTAT) +/// ``` #[derive(Debug, PartialEq)] pub struct ParamFilter { pub name: PropertyParameter, diff --git a/src/dav/decoder.rs b/aero-dav/src/decoder.rs similarity index 99% rename from src/dav/decoder.rs rename to aero-dav/src/decoder.rs index aa3c7e5..65cb712 100644 --- a/src/dav/decoder.rs +++ b/aero-dav/src/decoder.rs @@ -1,7 +1,6 @@ -use std::borrow::Cow; use std::future::Future; -use quick_xml::events::{Event, BytesStart, BytesDecl, BytesText}; +use quick_xml::events::Event; use quick_xml::events::attributes::AttrError; use quick_xml::name::{Namespace, QName, PrefixDeclaration, ResolveResult, ResolveResult::*}; use quick_xml::reader::NsReader; @@ -603,7 +602,7 @@ impl QRead for Href { mod tests { use super::*; use chrono::{FixedOffset, DateTime, TimeZone, Utc}; - use crate::dav::realization::Core; + use crate::realization::Core; #[tokio::test] async fn basic_propfind_propname() { diff --git a/src/dav/encoder.rs b/aero-dav/src/encoder.rs similarity index 99% rename from src/dav/encoder.rs rename to aero-dav/src/encoder.rs index 4de5440..fd2f9ca 100644 --- a/src/dav/encoder.rs +++ b/aero-dav/src/encoder.rs @@ -1,10 +1,5 @@ -use std::io::Cursor; - use quick_xml::Error as QError; -use quick_xml::events::{Event, BytesEnd, BytesStart, BytesText}; -use quick_xml::writer::ElementWriter; -use quick_xml::name::PrefixDeclaration; -use tokio::io::AsyncWrite; +use quick_xml::events::{Event, BytesText}; use super::types::*; use super::xml::{Node, Writer,QWrite,IWrite}; @@ -638,7 +633,7 @@ impl QWrite for Violation { #[cfg(test)] mod tests { use super::*; - use crate::dav::realization::Core; + use crate::realization::Core; use tokio::io::AsyncWriteExt; /// To run only the unit tests and avoid the behavior ones: diff --git a/src/dav/error.rs b/aero-dav/src/error.rs similarity index 100% rename from src/dav/error.rs rename to aero-dav/src/error.rs diff --git a/aero-dav/src/lib.rs b/aero-dav/src/lib.rs new file mode 100644 index 0000000..6bfbf62 --- /dev/null +++ b/aero-dav/src/lib.rs @@ -0,0 +1,25 @@ +#![feature(type_alias_impl_trait)] +#![feature(async_fn_in_trait)] +#![feature(async_closure)] +#![feature(trait_alias)] + +// utils +pub mod error; +pub mod xml; + +// webdav +pub mod types; +pub mod encoder; +pub mod decoder; + +// calendar +pub mod caltypes; +pub mod calencoder; +pub mod caldecoder; + +// wip +mod acltypes; +mod versioningtypes; + +// final type +pub mod realization; diff --git a/src/dav/realization.rs b/aero-dav/src/realization.rs similarity index 100% rename from src/dav/realization.rs rename to aero-dav/src/realization.rs diff --git a/src/dav/types.rs b/aero-dav/src/types.rs similarity index 99% rename from src/dav/types.rs rename to aero-dav/src/types.rs index 5ea38d1..2489c0a 100644 --- a/src/dav/types.rs +++ b/aero-dav/src/types.rs @@ -3,7 +3,6 @@ use std::fmt::Debug; use chrono::{DateTime,FixedOffset}; use super::xml; -use super::error; /// It's how we implement a DAV extension /// (That's the dark magic part...) diff --git a/src/dav/versioningtypes.rs b/aero-dav/src/versioningtypes.rs similarity index 100% rename from src/dav/versioningtypes.rs rename to aero-dav/src/versioningtypes.rs diff --git a/src/dav/xml.rs b/aero-dav/src/xml.rs similarity index 96% rename from src/dav/xml.rs rename to aero-dav/src/xml.rs index 02263fd..98037ac 100644 --- a/src/dav/xml.rs +++ b/aero-dav/src/xml.rs @@ -1,7 +1,8 @@ -use tokio::io::{AsyncWrite, AsyncBufRead}; -use quick_xml::events::{Event, BytesEnd, BytesStart, BytesText}; -use quick_xml::name::{Namespace, QName, PrefixDeclaration, ResolveResult, ResolveResult::*}; +use futures::Future; +use quick_xml::events::{Event, BytesStart}; +use quick_xml::name::ResolveResult; use quick_xml::reader::NsReader; +use tokio::io::{AsyncWrite, AsyncBufRead}; use super::error::ParsingError; @@ -16,10 +17,10 @@ pub trait IRead = AsyncBufRead + Unpin; // Serialization/Deserialization traits pub trait QWrite { - async fn qwrite(&self, xml: &mut Writer) -> Result<(), quick_xml::Error>; + fn qwrite(&self, xml: &mut Writer) -> impl Future>; } pub trait QRead { - async fn qread(xml: &mut Reader) -> Result; + fn qread(xml: &mut Reader) -> impl Future>; } // The representation of an XML node in Rust diff --git a/src/dav/mod.rs b/aero-proto/dav.rs similarity index 95% rename from src/dav/mod.rs rename to aero-proto/dav.rs index 906cfdd..fa2023a 100644 --- a/src/dav/mod.rs +++ b/aero-proto/dav.rs @@ -1,25 +1,3 @@ -// utils -pub mod error; -pub mod xml; - -// webdav -pub mod types; -pub mod encoder; -pub mod decoder; - -// calendar -mod caltypes; -mod calencoder; -mod caldecoder; - -// wip -mod acltypes; -mod versioningtypes; - -// final type -pub mod realization; - - use std::net::SocketAddr; use anyhow::{anyhow, Result}; diff --git a/src/imap/attributes.rs b/aero-proto/imap/attributes.rs similarity index 100% rename from src/imap/attributes.rs rename to aero-proto/imap/attributes.rs diff --git a/src/imap/capability.rs b/aero-proto/imap/capability.rs similarity index 100% rename from src/imap/capability.rs rename to aero-proto/imap/capability.rs diff --git a/src/imap/command/anonymous.rs b/aero-proto/imap/command/anonymous.rs similarity index 100% rename from src/imap/command/anonymous.rs rename to aero-proto/imap/command/anonymous.rs diff --git a/src/imap/command/anystate.rs b/aero-proto/imap/command/anystate.rs similarity index 100% rename from src/imap/command/anystate.rs rename to aero-proto/imap/command/anystate.rs diff --git a/src/imap/command/authenticated.rs b/aero-proto/imap/command/authenticated.rs similarity index 100% rename from src/imap/command/authenticated.rs rename to aero-proto/imap/command/authenticated.rs diff --git a/src/imap/command/mod.rs b/aero-proto/imap/command/mod.rs similarity index 100% rename from src/imap/command/mod.rs rename to aero-proto/imap/command/mod.rs diff --git a/src/imap/command/selected.rs b/aero-proto/imap/command/selected.rs similarity index 100% rename from src/imap/command/selected.rs rename to aero-proto/imap/command/selected.rs diff --git a/src/imap/flags.rs b/aero-proto/imap/flags.rs similarity index 100% rename from src/imap/flags.rs rename to aero-proto/imap/flags.rs diff --git a/src/imap/flow.rs b/aero-proto/imap/flow.rs similarity index 100% rename from src/imap/flow.rs rename to aero-proto/imap/flow.rs diff --git a/src/imap/imf_view.rs b/aero-proto/imap/imf_view.rs similarity index 100% rename from src/imap/imf_view.rs rename to aero-proto/imap/imf_view.rs diff --git a/src/imap/index.rs b/aero-proto/imap/index.rs similarity index 100% rename from src/imap/index.rs rename to aero-proto/imap/index.rs diff --git a/src/imap/mail_view.rs b/aero-proto/imap/mail_view.rs similarity index 100% rename from src/imap/mail_view.rs rename to aero-proto/imap/mail_view.rs diff --git a/src/imap/mailbox_view.rs b/aero-proto/imap/mailbox_view.rs similarity index 100% rename from src/imap/mailbox_view.rs rename to aero-proto/imap/mailbox_view.rs diff --git a/src/imap/mime_view.rs b/aero-proto/imap/mime_view.rs similarity index 100% rename from src/imap/mime_view.rs rename to aero-proto/imap/mime_view.rs diff --git a/src/imap/mod.rs b/aero-proto/imap/mod.rs similarity index 100% rename from src/imap/mod.rs rename to aero-proto/imap/mod.rs diff --git a/src/imap/request.rs b/aero-proto/imap/request.rs similarity index 100% rename from src/imap/request.rs rename to aero-proto/imap/request.rs diff --git a/src/imap/response.rs b/aero-proto/imap/response.rs similarity index 100% rename from src/imap/response.rs rename to aero-proto/imap/response.rs diff --git a/src/imap/search.rs b/aero-proto/imap/search.rs similarity index 100% rename from src/imap/search.rs rename to aero-proto/imap/search.rs diff --git a/src/imap/session.rs b/aero-proto/imap/session.rs similarity index 100% rename from src/imap/session.rs rename to aero-proto/imap/session.rs diff --git a/src/lmtp.rs b/aero-proto/lmtp.rs similarity index 100% rename from src/lmtp.rs rename to aero-proto/lmtp.rs diff --git a/aero-proto/sasl.rs b/aero-proto/sasl.rs new file mode 100644 index 0000000..fe292e1 --- /dev/null +++ b/aero-proto/sasl.rs @@ -0,0 +1,140 @@ +use std::net::SocketAddr; + +use anyhow::{anyhow, bail, Result}; +use futures::stream::{FuturesUnordered, StreamExt}; +use tokio::io::BufStream; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::sync::watch; + +use aero_user::config::AuthConfig; +use aero_user::login::ArcLoginProvider; + + +pub struct AuthServer { + login_provider: ArcLoginProvider, + bind_addr: SocketAddr, +} + +impl AuthServer { + pub fn new(config: AuthConfig, login_provider: ArcLoginProvider) -> Self { + Self { + bind_addr: config.bind_addr, + login_provider, + } + } + + pub async fn run(self: Self, mut must_exit: watch::Receiver) -> Result<()> { + let tcp = TcpListener::bind(self.bind_addr).await?; + tracing::info!( + "SASL Authentication Protocol listening on {:#}", + self.bind_addr + ); + + let mut connections = FuturesUnordered::new(); + + while !*must_exit.borrow() { + let wait_conn_finished = async { + if connections.is_empty() { + futures::future::pending().await + } else { + connections.next().await + } + }; + + let (socket, remote_addr) = tokio::select! { + a = tcp.accept() => a?, + _ = wait_conn_finished => continue, + _ = must_exit.changed() => continue, + }; + + tracing::info!("AUTH: accepted connection from {}", remote_addr); + let conn = tokio::spawn( + NetLoop::new(socket, self.login_provider.clone(), must_exit.clone()).run_error(), + ); + + connections.push(conn); + } + drop(tcp); + + tracing::info!("AUTH server shutting down, draining remaining connections..."); + while connections.next().await.is_some() {} + + Ok(()) + } +} + +struct NetLoop { + login: ArcLoginProvider, + stream: BufStream, + stop: watch::Receiver, + state: State, + read_buf: Vec, + write_buf: BytesMut, +} + +impl NetLoop { + fn new(stream: TcpStream, login: ArcLoginProvider, stop: watch::Receiver) -> Self { + Self { + login, + stream: BufStream::new(stream), + state: State::Init, + stop, + read_buf: Vec::new(), + write_buf: BytesMut::new(), + } + } + + async fn run_error(self) { + match self.run().await { + Ok(()) => tracing::info!("Auth session succeeded"), + Err(e) => tracing::error!(err=?e, "Auth session failed"), + } + } + + async fn run(mut self) -> Result<()> { + loop { + tokio::select! { + read_res = self.stream.read_until(b'\n', &mut self.read_buf) => { + // Detect EOF / socket close + let bread = read_res?; + if bread == 0 { + tracing::info!("Reading buffer empty, connection has been closed. Exiting AUTH session."); + return Ok(()) + } + + // Parse command + let (_, cmd) = client_command(&self.read_buf).map_err(|_| anyhow!("Unable to parse command"))?; + tracing::trace!(cmd=?cmd, "Received command"); + + // Make some progress in our local state + self.state.progress(cmd, &self.login).await; + if matches!(self.state, State::Error) { + bail!("Internal state is in error, previous logs explain what went wrong"); + } + + // Build response + let srv_cmds = self.state.response(); + srv_cmds.iter().try_for_each(|r| { + tracing::trace!(cmd=?r, "Sent command"); + r.encode(&mut self.write_buf) + })?; + + // Send responses if at least one command response has been generated + if !srv_cmds.is_empty() { + self.stream.write_all(&self.write_buf).await?; + self.stream.flush().await?; + } + + // Reset buffers + self.read_buf.clear(); + self.write_buf.clear(); + }, + _ = self.stop.changed() => { + tracing::debug!("Server is stopping, quitting this runner"); + return Ok(()) + } + } + } + } +} diff --git a/aero-sasl/Cargo.toml b/aero-sasl/Cargo.toml new file mode 100644 index 0000000..3e66ff3 --- /dev/null +++ b/aero-sasl/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "aero-sasl" +version = "0.3.0" +authors = ["Alex Auvolat ", "Quentin Dufour "] +edition = "2021" +license = "EUPL-1.2" +description = "A partial and standalone implementation of the Dovecot SASL Auth Protocol" + +[dependencies] + +anyhow.workspace = true +base64.workspace = true +futures.workspace = true +nom.workspace = true +rand.workspace = true +tokio.workspace = true +tokio-util.workspace = true +tracing.workspace = true +hex.workspace = true + +#log.workspace = true +#serde.workspace = true diff --git a/aero-sasl/src/decode.rs b/aero-sasl/src/decode.rs new file mode 100644 index 0000000..f5d7b53 --- /dev/null +++ b/aero-sasl/src/decode.rs @@ -0,0 +1,243 @@ +use base64::Engine; +use nom::{ + branch::alt, + bytes::complete::{tag, tag_no_case, take, take_while, take_while1}, + character::complete::{tab, u16, u64}, + combinator::{map, opt, recognize, rest, value}, + error::{Error, ErrorKind}, + multi::{many1, separated_list0}, + sequence::{pair, preceded, tuple}, + IResult, +}; + +use super::types::*; + +pub fn client_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> { + alt((version_command, cpid_command, auth_command, cont_command))(input) +} + +/* +fn server_command(buf: &u8) -> IResult<&u8, ServerCommand> { + unimplemented!(); +} +*/ + +// --------------------- + +fn version_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> { + let mut parser = tuple((tag_no_case(b"VERSION"), tab, u64, tab, u64)); + + let (input, (_, _, major, _, minor)) = parser(input)?; + Ok((input, ClientCommand::Version(Version { major, minor }))) +} + +pub fn cpid_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> { + preceded( + pair(tag_no_case(b"CPID"), tab), + map(u64, |v| ClientCommand::Cpid(v)), + )(input) +} + +fn mechanism<'a>(input: &'a [u8]) -> IResult<&'a [u8], Mechanism> { + alt(( + value(Mechanism::Plain, tag_no_case(b"PLAIN")), + value(Mechanism::Login, tag_no_case(b"LOGIN")), + ))(input) +} + +fn is_not_tab_or_esc_or_lf(c: u8) -> bool { + c != 0x09 && c != 0x01 && c != 0x0a // TAB or 0x01 or LF +} + +fn is_esc<'a>(input: &'a [u8]) -> IResult<&'a [u8], &[u8]> { + preceded(tag(&[0x01]), take(1usize))(input) +} + +fn parameter<'a>(input: &'a [u8]) -> IResult<&'a [u8], &[u8]> { + recognize(many1(alt((take_while1(is_not_tab_or_esc_or_lf), is_esc))))(input) +} + +fn parameter_str(input: &[u8]) -> IResult<&[u8], String> { + let (input, buf) = parameter(input)?; + + std::str::from_utf8(buf) + .map(|v| (input, v.to_string())) + .map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1))) +} + +fn is_param_name_char(c: u8) -> bool { + is_not_tab_or_esc_or_lf(c) && c != 0x3d // = +} + +fn parameter_name(input: &[u8]) -> IResult<&[u8], String> { + let (input, buf) = take_while1(is_param_name_char)(input)?; + + std::str::from_utf8(buf) + .map(|v| (input, v.to_string())) + .map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1))) +} + +fn service<'a>(input: &'a [u8]) -> IResult<&'a [u8], String> { + preceded(tag_no_case("service="), parameter_str)(input) +} + +fn auth_option<'a>(input: &'a [u8]) -> IResult<&'a [u8], AuthOption> { + use AuthOption::*; + alt(( + alt(( + value(Debug, tag_no_case(b"debug")), + value(NoPenalty, tag_no_case(b"no-penalty")), + value(ClientId, tag_no_case(b"client_id")), + value(NoLogin, tag_no_case(b"nologin")), + map(preceded(tag_no_case(b"session="), u64), |id| Session(id)), + map(preceded(tag_no_case(b"lip="), parameter_str), |ip| { + LocalIp(ip) + }), + map(preceded(tag_no_case(b"rip="), parameter_str), |ip| { + RemoteIp(ip) + }), + map(preceded(tag_no_case(b"lport="), u16), |port| { + LocalPort(port) + }), + map(preceded(tag_no_case(b"rport="), u16), |port| { + RemotePort(port) + }), + map(preceded(tag_no_case(b"real_rip="), parameter_str), |ip| { + RealRemoteIp(ip) + }), + map(preceded(tag_no_case(b"real_lip="), parameter_str), |ip| { + RealLocalIp(ip) + }), + map(preceded(tag_no_case(b"real_lport="), u16), |port| { + RealLocalPort(port) + }), + map(preceded(tag_no_case(b"real_rport="), u16), |port| { + RealRemotePort(port) + }), + )), + alt(( + map( + preceded(tag_no_case(b"local_name="), parameter_str), + |name| LocalName(name), + ), + map( + preceded(tag_no_case(b"forward_views="), parameter), + |views| ForwardViews(views.into()), + ), + map(preceded(tag_no_case(b"secured="), parameter_str), |info| { + Secured(Some(info)) + }), + value(Secured(None), tag_no_case(b"secured")), + value(CertUsername, tag_no_case(b"cert_username")), + map(preceded(tag_no_case(b"transport="), parameter_str), |ts| { + Transport(ts) + }), + map( + preceded(tag_no_case(b"tls_cipher="), parameter_str), + |cipher| TlsCipher(cipher), + ), + map( + preceded(tag_no_case(b"tls_cipher_bits="), parameter_str), + |bits| TlsCipherBits(bits), + ), + map(preceded(tag_no_case(b"tls_pfs="), parameter_str), |pfs| { + TlsPfs(pfs) + }), + map( + preceded(tag_no_case(b"tls_protocol="), parameter_str), + |proto| TlsProtocol(proto), + ), + map( + preceded(tag_no_case(b"valid-client-cert="), parameter_str), + |cert| ValidClientCert(cert), + ), + )), + alt(( + map(preceded(tag_no_case(b"resp="), base64), |data| Resp(data)), + map( + tuple((parameter_name, tag(b"="), parameter)), + |(n, _, v)| UnknownPair(n, v.into()), + ), + map(parameter, |v| UnknownBool(v.into())), + )), + ))(input) +} + +fn auth_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> { + let mut parser = tuple(( + tag_no_case(b"AUTH"), + tab, + u64, + tab, + mechanism, + tab, + service, + map(opt(preceded(tab, separated_list0(tab, auth_option))), |o| { + o.unwrap_or(vec![]) + }), + )); + let (input, (_, _, id, _, mech, _, service, options)) = parser(input)?; + Ok(( + input, + ClientCommand::Auth { + id, + mech, + service, + options, + }, + )) +} + +fn is_base64_core(c: u8) -> bool { + c >= 0x30 && c <= 0x39 // 0-9 + || c >= 0x41 && c <= 0x5a // A-Z + || c >= 0x61 && c <= 0x7a // a-z + || c == 0x2b // + + || c == 0x2f // / +} + +fn is_base64_pad(c: u8) -> bool { + c == 0x3d // = +} + +fn base64(input: &[u8]) -> IResult<&[u8], Vec> { + let (input, (b64, _)) = tuple((take_while1(is_base64_core), take_while(is_base64_pad)))(input)?; + + let data = base64::engine::general_purpose::STANDARD_NO_PAD + .decode(b64) + .map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1)))?; + + Ok((input, data)) +} + +/// @FIXME Dovecot does not say if base64 content must be padded or not +fn cont_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> { + let mut parser = tuple((tag_no_case(b"CONT"), tab, u64, tab, base64)); + + let (input, (_, _, id, _, data)) = parser(input)?; + Ok((input, ClientCommand::Cont { id, data })) +} + +// ----------------------------------------------------------------- +// +// SASL DECODING +// +// ----------------------------------------------------------------- + +fn not_null(c: u8) -> bool { + c != 0x0 +} + +// impersonated user, login, password +pub fn auth_plain<'a>(input: &'a [u8]) -> IResult<&'a [u8], (&'a [u8], &'a [u8], &'a [u8])> { + map( + tuple(( + take_while(not_null), + take(1usize), + take_while(not_null), + take(1usize), + rest, + )), + |(imp, _, user, _, pass)| (imp, user, pass), + )(input) +} diff --git a/aero-sasl/src/encode.rs b/aero-sasl/src/encode.rs new file mode 100644 index 0000000..625d035 --- /dev/null +++ b/aero-sasl/src/encode.rs @@ -0,0 +1,157 @@ +use anyhow::Result; +use base64::Engine; +use tokio_util::bytes::{BufMut, BytesMut}; + +use super::types::*; + +pub trait Encode { + fn encode(&self, out: &mut BytesMut) -> Result<()>; +} + +fn tab_enc(out: &mut BytesMut) { + out.put(&[0x09][..]) +} + +fn lf_enc(out: &mut BytesMut) { + out.put(&[0x0A][..]) +} + +impl Encode for Mechanism { + fn encode(&self, out: &mut BytesMut) -> Result<()> { + match self { + Self::Plain => out.put(&b"PLAIN"[..]), + Self::Login => out.put(&b"LOGIN"[..]), + } + Ok(()) + } +} + +impl Encode for MechanismParameters { + fn encode(&self, out: &mut BytesMut) -> Result<()> { + match self { + Self::Anonymous => out.put(&b"anonymous"[..]), + Self::PlainText => out.put(&b"plaintext"[..]), + Self::Dictionary => out.put(&b"dictionary"[..]), + Self::Active => out.put(&b"active"[..]), + Self::ForwardSecrecy => out.put(&b"forward-secrecy"[..]), + Self::MutualAuth => out.put(&b"mutual-auth"[..]), + Self::Private => out.put(&b"private"[..]), + } + Ok(()) + } +} + +impl Encode for FailCode { + fn encode(&self, out: &mut BytesMut) -> Result<()> { + match self { + Self::TempFail => out.put(&b"temp_fail"[..]), + Self::AuthzFail => out.put(&b"authz_fail"[..]), + Self::UserDisabled => out.put(&b"user_disabled"[..]), + Self::PassExpired => out.put(&b"pass_expired"[..]), + }; + Ok(()) + } +} + +impl Encode for ServerCommand { + fn encode(&self, out: &mut BytesMut) -> Result<()> { + match self { + Self::Version(Version { major, minor }) => { + out.put(&b"VERSION"[..]); + tab_enc(out); + out.put(major.to_string().as_bytes()); + tab_enc(out); + out.put(minor.to_string().as_bytes()); + lf_enc(out); + } + Self::Spid(pid) => { + out.put(&b"SPID"[..]); + tab_enc(out); + out.put(pid.to_string().as_bytes()); + lf_enc(out); + } + Self::Cuid(pid) => { + out.put(&b"CUID"[..]); + tab_enc(out); + out.put(pid.to_string().as_bytes()); + lf_enc(out); + } + Self::Cookie(cval) => { + out.put(&b"COOKIE"[..]); + tab_enc(out); + out.put(hex::encode(cval).as_bytes()); + lf_enc(out); + } + Self::Mech { kind, parameters } => { + out.put(&b"MECH"[..]); + tab_enc(out); + kind.encode(out)?; + for p in parameters.iter() { + tab_enc(out); + p.encode(out)?; + } + lf_enc(out); + } + Self::Done => { + out.put(&b"DONE"[..]); + lf_enc(out); + } + Self::Cont { id, data } => { + out.put(&b"CONT"[..]); + tab_enc(out); + out.put(id.to_string().as_bytes()); + tab_enc(out); + if let Some(rdata) = data { + let b64 = base64::engine::general_purpose::STANDARD.encode(rdata); + out.put(b64.as_bytes()); + } + lf_enc(out); + } + Self::Ok { + id, + user_id, + extra_parameters, + } => { + out.put(&b"OK"[..]); + tab_enc(out); + out.put(id.to_string().as_bytes()); + if let Some(user) = user_id { + tab_enc(out); + out.put(&b"user="[..]); + out.put(user.as_bytes()); + } + for p in extra_parameters.iter() { + tab_enc(out); + out.put(&p[..]); + } + lf_enc(out); + } + Self::Fail { + id, + user_id, + code, + extra_parameters, + } => { + out.put(&b"FAIL"[..]); + tab_enc(out); + out.put(id.to_string().as_bytes()); + if let Some(user) = user_id { + tab_enc(out); + out.put(&b"user="[..]); + out.put(user.as_bytes()); + } + if let Some(code_val) = code { + tab_enc(out); + out.put(&b"code="[..]); + code_val.encode(out)?; + } + for p in extra_parameters.iter() { + tab_enc(out); + out.put(&p[..]); + } + lf_enc(out); + } + } + Ok(()) + } +} diff --git a/aero-sasl/src/flow.rs b/aero-sasl/src/flow.rs new file mode 100644 index 0000000..6cc698a --- /dev/null +++ b/aero-sasl/src/flow.rs @@ -0,0 +1,201 @@ +use futures::Future; +use rand::prelude::*; + +use super::types::*; +use super::decode::auth_plain; + +#[derive(Debug)] +pub enum AuthRes { + Success(String), + Failed(Option, Option), +} + +#[derive(Debug)] +pub enum State { + Error, + Init, + HandshakePart(Version), + HandshakeDone, + AuthPlainProgress { id: u64 }, + AuthDone { id: u64, res: AuthRes }, +} + +const SERVER_MAJOR: u64 = 1; +const SERVER_MINOR: u64 = 2; +const EMPTY_AUTHZ: &[u8] = &[]; +impl State { + pub fn new() -> Self { + Self::Init + } + + async fn try_auth_plain<'a, X, F>(&self, data: &'a [u8], login: X) -> AuthRes + where + X: FnOnce(&'a str, &'a str) -> F, + F: Future, + { + // Check that we can extract user's login+pass + let (ubin, pbin) = match auth_plain(&data) { + Ok(([], (authz, user, pass))) if authz == user || authz == EMPTY_AUTHZ => (user, pass), + Ok(_) => { + tracing::error!("Impersonating user is not supported"); + return AuthRes::Failed(None, None); + } + Err(e) => { + tracing::error!(err=?e, "Could not parse the SASL PLAIN data chunk"); + return AuthRes::Failed(None, None); + } + }; + + // Try to convert it to UTF-8 + let (user, password) = match (std::str::from_utf8(ubin), std::str::from_utf8(pbin)) { + (Ok(u), Ok(p)) => (u, p), + _ => { + tracing::error!("Username or password contain invalid UTF-8 characters"); + return AuthRes::Failed(None, None); + } + }; + + // Try to connect user + match login(user, password).await { + true => AuthRes::Success(user.to_string()), + false => { + tracing::warn!("login failed"); + AuthRes::Failed(Some(user.to_string()), None) + } + } + } + + pub async fn progress(&mut self, cmd: ClientCommand, login: X) + where + X: FnOnce(&str, &str) -> F, + F: Future, + { + let new_state = 'state: { + match (std::mem::replace(self, State::Error), cmd) { + (Self::Init, ClientCommand::Version(v)) => Self::HandshakePart(v), + (Self::HandshakePart(version), ClientCommand::Cpid(_cpid)) => { + if version.major != SERVER_MAJOR { + tracing::error!( + client_major = version.major, + server_major = SERVER_MAJOR, + "Unsupported client major version" + ); + break 'state Self::Error; + } + + Self::HandshakeDone + } + ( + Self::HandshakeDone { .. }, + ClientCommand::Auth { + id, mech, options, .. + }, + ) + | ( + Self::AuthDone { .. }, + ClientCommand::Auth { + id, mech, options, .. + }, + ) => { + if mech != Mechanism::Plain { + tracing::error!(mechanism=?mech, "Unsupported Authentication Mechanism"); + break 'state Self::AuthDone { + id, + res: AuthRes::Failed(None, None), + }; + } + + match options.last() { + Some(AuthOption::Resp(data)) => Self::AuthDone { + id, + res: self.try_auth_plain(&data, login).await, + }, + _ => Self::AuthPlainProgress { id }, + } + } + (Self::AuthPlainProgress { id }, ClientCommand::Cont { id: cid, data }) => { + // Check that ID matches + if cid != id { + tracing::error!( + auth_id = id, + cont_id = cid, + "CONT id does not match AUTH id" + ); + break 'state Self::AuthDone { + id, + res: AuthRes::Failed(None, None), + }; + } + + Self::AuthDone { + id, + res: self.try_auth_plain(&data, login).await, + } + } + _ => { + tracing::error!("This command is not valid in this context"); + Self::Error + } + } + }; + tracing::debug!(state=?new_state, "Made progress"); + *self = new_state; + } + + pub fn response(&self) -> Vec { + let mut srv_cmd: Vec = Vec::new(); + + match self { + Self::HandshakeDone { .. } => { + srv_cmd.push(ServerCommand::Version(Version { + major: SERVER_MAJOR, + minor: SERVER_MINOR, + })); + + srv_cmd.push(ServerCommand::Mech { + kind: Mechanism::Plain, + parameters: vec![MechanismParameters::PlainText], + }); + + srv_cmd.push(ServerCommand::Spid(15u64)); + srv_cmd.push(ServerCommand::Cuid(19350u64)); + + let mut cookie = [0u8; 16]; + thread_rng().fill(&mut cookie); + srv_cmd.push(ServerCommand::Cookie(cookie)); + + srv_cmd.push(ServerCommand::Done); + } + Self::AuthPlainProgress { id } => { + srv_cmd.push(ServerCommand::Cont { + id: *id, + data: None, + }); + } + Self::AuthDone { + id, + res: AuthRes::Success(user), + } => { + srv_cmd.push(ServerCommand::Ok { + id: *id, + user_id: Some(user.to_string()), + extra_parameters: vec![], + }); + } + Self::AuthDone { + id, + res: AuthRes::Failed(maybe_user, maybe_failcode), + } => { + srv_cmd.push(ServerCommand::Fail { + id: *id, + user_id: maybe_user.clone(), + code: maybe_failcode.clone(), + extra_parameters: vec![], + }); + } + _ => (), + }; + + srv_cmd + } +} diff --git a/aero-sasl/src/lib.rs b/aero-sasl/src/lib.rs new file mode 100644 index 0000000..230862a --- /dev/null +++ b/aero-sasl/src/lib.rs @@ -0,0 +1,43 @@ +/// Seek compatibility with the Dovecot Authentication Protocol +/// +/// ## Trace +/// +/// ```text +/// S: VERSION 1 2 +/// S: MECH PLAIN plaintext +/// S: MECH LOGIN plaintext +/// S: SPID 15 +/// S: CUID 17654 +/// S: COOKIE f56692bee41f471ed01bd83520025305 +/// S: DONE +/// C: VERSION 1 2 +/// C: CPID 1 +/// +/// C: AUTH 2 PLAIN service=smtp +/// S: CONT 2 +/// C: CONT 2 base64stringFollowingRFC4616== +/// S: OK 2 user=alice@example.tld +/// +/// C: AUTH 42 LOGIN service=smtp +/// S: CONT 42 VXNlcm5hbWU6 +/// C: CONT 42 b64User +/// S: CONT 42 UGFzc3dvcmQ6 +/// C: CONT 42 b64Pass +/// S: FAIL 42 user=alice +/// ``` +/// +/// ## RFC References +/// +/// PLAIN SASL - https://datatracker.ietf.org/doc/html/rfc4616 +/// +/// +/// ## Dovecot References +/// +/// https://doc.dovecot.org/developer_manual/design/auth_protocol/ +/// https://doc.dovecot.org/configuration_manual/authentication/authentication_mechanisms/#authentication-authentication-mechanisms +/// https://doc.dovecot.org/configuration_manual/howto/simple_virtual_install/#simple-virtual-install-smtp-auth +/// https://doc.dovecot.org/configuration_manual/howto/postfix_and_dovecot_sasl/#howto-postfix-and-dovecot-sasl +pub mod types; +pub mod encode; +pub mod decode; +pub mod flow; diff --git a/aero-sasl/src/types.rs b/aero-sasl/src/types.rs new file mode 100644 index 0000000..d71405e --- /dev/null +++ b/aero-sasl/src/types.rs @@ -0,0 +1,163 @@ +#[derive(Debug, Clone, PartialEq)] +pub enum Mechanism { + Plain, + Login, +} + +#[derive(Clone, Debug)] +pub enum AuthOption { + /// Unique session ID. Mainly used for logging. + Session(u64), + /// Local IP connected to by the client. In standard string format, e.g. 127.0.0.1 or ::1. + LocalIp(String), + /// Remote client IP + RemoteIp(String), + /// Local port connected to by the client. + LocalPort(u16), + /// Remote client port + RemotePort(u16), + /// When Dovecot proxy is used, the real_rip/real_port are the proxy’s IP/port and real_lip/real_lport are the backend’s IP/port where the proxy was connected to. + RealRemoteIp(String), + RealLocalIp(String), + RealLocalPort(u16), + RealRemotePort(u16), + /// TLS SNI name + LocalName(String), + /// Enable debugging for this lookup. + Debug, + /// List of fields that will become available via %{forward_*} variables. The list is double-tab-escaped, like: tab_escaped[tab_escaped(key=value)[...] + /// Note: we do not unescape the tabulation, and thus we don't parse the data + ForwardViews(Vec), + /// Remote user has secured transport to auth client (e.g. localhost, SSL, TLS). + Secured(Option), + /// The value can be “insecure”, “trusted” or “TLS”. + Transport(String), + /// TLS cipher being used. + TlsCipher(String), + /// The number of bits in the TLS cipher. + /// @FIXME: I don't know how if it's a string or an integer + TlsCipherBits(String), + /// TLS perfect forward secrecy algorithm (e.g. DH, ECDH) + TlsPfs(String), + /// TLS protocol name (e.g. SSLv3, TLSv1.2) + TlsProtocol(String), + /// Remote user has presented a valid SSL certificate. + ValidClientCert(String), + /// Ignore auth penalty tracking for this request + NoPenalty, + /// Unknown option sent by Postfix + NoLogin, + /// Username taken from client’s SSL certificate. + CertUsername, + /// IMAP ID string + ClientId, + /// An unknown key + UnknownPair(String, Vec), + UnknownBool(Vec), + /// Initial response for authentication mechanism. + /// NOTE: This must be the last parameter. Everything after it is ignored. + /// This is to avoid accidental security holes if user-given data is directly put to base64 string without filtering out tabs. + /// **This field is used when the data to pass is small, it's a way to "inline a continuation". + Resp(Vec), +} + +#[derive(Debug, Clone)] +pub struct Version { + pub major: u64, + pub minor: u64, +} + +#[derive(Debug)] +pub enum ClientCommand { + /// Both client and server should check that they support the same major version number. If they don’t, the other side isn’t expected to be talking the same protocol and should be disconnected. Minor version can be ignored. This document specifies the version number 1.2. + Version(Version), + /// CPID finishes the handshake from client. + Cpid(u64), + Auth { + /// ID is a connection-specific unique request identifier. It must be a 32bit number, so typically you’d just increment it by one. + id: u64, + /// A SASL mechanism (eg. LOGIN, PLAIN, etc.) + /// See: https://doc.dovecot.org/configuration_manual/authentication/authentication_mechanisms/#authentication-authentication-mechanisms + mech: Mechanism, + /// Service is the service requesting authentication, eg. pop3, imap, smtp. + service: String, + /// All the optional parameters + options: Vec, + }, + Cont { + /// The must match the of the AUTH command. + id: u64, + /// Data that will be serialized to / deserialized from base64 + data: Vec, + }, +} + +#[derive(Debug)] +pub enum MechanismParameters { + /// Anonymous authentication + Anonymous, + /// Transfers plaintext passwords + PlainText, + /// Subject to passive (dictionary) attack + Dictionary, + /// Subject to active (non-dictionary) attack + Active, + /// Provides forward secrecy between sessions + ForwardSecrecy, + /// Provides mutual authentication + MutualAuth, + /// Don’t advertise this as available SASL mechanism (eg. APOP) + Private, +} + +#[derive(Debug, Clone)] +pub enum FailCode { + /// This is a temporary internal failure, e.g. connection was lost to SQL database. + TempFail, + /// Authentication succeeded, but authorization failed (master user’s password was ok, but destination user was not ok). + AuthzFail, + /// User is disabled (password may or may not have been correct) + UserDisabled, + /// User’s password has expired. + PassExpired, +} + +#[derive(Debug)] +pub enum ServerCommand { + /// Both client and server should check that they support the same major version number. If they don’t, the other side isn’t expected to be talking the same protocol and should be disconnected. Minor version can be ignored. This document specifies the version number 1.2. + Version(Version), + /// CPID and SPID specify client and server Process Identifiers (PIDs). They should be unique identifiers for the specific process. UNIX process IDs are good choices. + /// SPID can be used by authentication client to tell master which server process handled the authentication. + Spid(u64), + /// CUID is a server process-specific unique connection identifier. It’s different each time a connection is established for the server. + /// CUID is currently useful only for APOP authentication. + Cuid(u64), + Mech { + kind: Mechanism, + parameters: Vec, + }, + /// COOKIE returns connection-specific 128 bit cookie in hex. It must be given to REQUEST command. (Protocol v1.1+ / Dovecot v2.0+) + Cookie([u8; 16]), + /// DONE finishes the handshake from server. + Done, + + Fail { + id: u64, + user_id: Option, + code: Option, + extra_parameters: Vec>, + }, + Cont { + id: u64, + data: Option>, + }, + /// FAIL and OK may contain multiple unspecified parameters which authentication client may handle specially. + /// The only one specified here is user= parameter, which should always be sent if the userid is known. + Ok { + id: u64, + user_id: Option, + extra_parameters: Vec>, + }, +} + + diff --git a/aero-user/Cargo.toml b/aero-user/Cargo.toml new file mode 100644 index 0000000..fc851e2 --- /dev/null +++ b/aero-user/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "aero-user" +version = "0.3.0" +authors = ["Alex Auvolat ", "Quentin Dufour "] +edition = "2021" +license = "EUPL-1.2" +description = "Represent an encrypted user profile" + +[dependencies] +anyhow.workspace = true +serde.workspace = true +zstd.workspace = true +sodiumoxide.workspace = true +log.workspace = true +async-trait.workspace = true +ldap3.workspace = true +base64.workspace = true +rand.workspace = true +tokio.workspace = true +aws-config.workspace = true +aws-sdk-s3.workspace = true +aws-smithy-runtime.workspace = true +aws-smithy-runtime-api.workspace = true +hyper-rustls.workspace = true +hyper-util.workspace = true +k2v-client.workspace = true +rmp-serde.workspace = true +toml.workspace = true +tracing.workspace = true +argon2.workspace = true diff --git a/src/config.rs b/aero-user/src/config.rs similarity index 100% rename from src/config.rs rename to aero-user/src/config.rs diff --git a/src/cryptoblob.rs b/aero-user/src/cryptoblob.rs similarity index 100% rename from src/cryptoblob.rs rename to aero-user/src/cryptoblob.rs diff --git a/aero-user/src/lib.rs b/aero-user/src/lib.rs new file mode 100644 index 0000000..9b08fe2 --- /dev/null +++ b/aero-user/src/lib.rs @@ -0,0 +1,9 @@ +pub mod config; +pub mod cryptoblob; +pub mod login; +pub mod storage; + +// A user is composed of 3 things: +// - An identity (login) +// - A storage profile (storage) +// - Some cryptography data (cryptoblob) diff --git a/src/login/demo_provider.rs b/aero-user/src/login/demo_provider.rs similarity index 100% rename from src/login/demo_provider.rs rename to aero-user/src/login/demo_provider.rs diff --git a/src/login/ldap_provider.rs b/aero-user/src/login/ldap_provider.rs similarity index 99% rename from src/login/ldap_provider.rs rename to aero-user/src/login/ldap_provider.rs index 0af5676..ca5a356 100644 --- a/src/login/ldap_provider.rs +++ b/aero-user/src/login/ldap_provider.rs @@ -1,11 +1,10 @@ -use anyhow::Result; use async_trait::async_trait; use ldap3::{LdapConnAsync, Scope, SearchEntry}; use log::debug; use crate::config::*; -use crate::login::*; use crate::storage; +use super::*; pub struct LdapLoginProvider { ldap_server: String, diff --git a/src/login/mod.rs b/aero-user/src/login/mod.rs similarity index 100% rename from src/login/mod.rs rename to aero-user/src/login/mod.rs index 4a1dee1..5e54b4a 100644 --- a/src/login/mod.rs +++ b/aero-user/src/login/mod.rs @@ -2,11 +2,11 @@ pub mod demo_provider; pub mod ldap_provider; pub mod static_provider; -use base64::Engine; use std::sync::Arc; use anyhow::{anyhow, bail, Context, Result}; use async_trait::async_trait; +use base64::Engine; use rand::prelude::*; use crate::cryptoblob::*; diff --git a/src/login/static_provider.rs b/aero-user/src/login/static_provider.rs similarity index 99% rename from src/login/static_provider.rs rename to aero-user/src/login/static_provider.rs index 79626df..ed39343 100644 --- a/src/login/static_provider.rs +++ b/aero-user/src/login/static_provider.rs @@ -1,12 +1,11 @@ use std::collections::HashMap; use std::path::PathBuf; -use std::sync::Arc; + +use anyhow::{anyhow, bail}; +use async_trait::async_trait; use tokio::signal::unix::{signal, SignalKind}; use tokio::sync::watch; -use anyhow::{anyhow, bail, Result}; -use async_trait::async_trait; - use crate::config::*; use crate::login::*; use crate::storage; diff --git a/src/storage/garage.rs b/aero-user/src/storage/garage.rs similarity index 99% rename from src/storage/garage.rs rename to aero-user/src/storage/garage.rs index 7152764..7e930c3 100644 --- a/src/storage/garage.rs +++ b/aero-user/src/storage/garage.rs @@ -6,7 +6,7 @@ use hyper_util::client::legacy::{connect::HttpConnector, Client as HttpClient}; use hyper_util::rt::TokioExecutor; use serde::Serialize; -use crate::storage::*; +use super::*; pub struct GarageRoot { k2v_http: HttpClient, k2v_client::Body>, diff --git a/src/storage/in_memory.rs b/aero-user/src/storage/in_memory.rs similarity index 99% rename from src/storage/in_memory.rs rename to aero-user/src/storage/in_memory.rs index 3c3a94c..a676797 100644 --- a/src/storage/in_memory.rs +++ b/aero-user/src/storage/in_memory.rs @@ -1,7 +1,7 @@ use crate::storage::*; -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::ops::Bound::{self, Excluded, Included, Unbounded}; -use std::sync::{Arc, RwLock}; +use std::sync::RwLock; use tokio::sync::Notify; /// This implementation is very inneficient, and not completely correct diff --git a/src/storage/mod.rs b/aero-user/src/storage/mod.rs similarity index 99% rename from src/storage/mod.rs rename to aero-user/src/storage/mod.rs index 1f86f71..f5eb8d3 100644 --- a/src/storage/mod.rs +++ b/aero-user/src/storage/mod.rs @@ -11,11 +11,12 @@ pub mod garage; pub mod in_memory; -use async_trait::async_trait; use std::collections::HashMap; use std::hash::Hash; use std::sync::Arc; +use async_trait::async_trait; + #[derive(Debug, Clone)] pub enum Alternative { Tombstone, diff --git a/aerogramme/Cargo.toml b/aerogramme/Cargo.toml new file mode 100644 index 0000000..e408aec --- /dev/null +++ b/aerogramme/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "aerogramme" +version = "0.3.0" +authors = ["Alex Auvolat ", "Quentin Dufour "] +edition = "2021" +license = "EUPL-1.2" +description = "A robust email server" + +[[test]] +name = "behavior" +path = "tests/behavior.rs" +harness = false diff --git a/src/k2v_util.rs b/aerogramme/src/k2v_util.rs similarity index 100% rename from src/k2v_util.rs rename to aerogramme/src/k2v_util.rs diff --git a/src/lib.rs b/aerogramme/src/lib.rs similarity index 100% rename from src/lib.rs rename to aerogramme/src/lib.rs diff --git a/src/main.rs b/aerogramme/src/main.rs similarity index 100% rename from src/main.rs rename to aerogramme/src/main.rs diff --git a/src/server.rs b/aerogramme/src/server.rs similarity index 100% rename from src/server.rs rename to aerogramme/src/server.rs diff --git a/flake.nix b/flake.nix index 01dfda1..c6ae4ce 100644 --- a/flake.nix +++ b/flake.nix @@ -186,12 +186,12 @@ shell = gpkgs.mkShell { buildInputs = [ cargo2nix.packages.x86_64-linux.default - fenix.packages.x86_64-linux.minimal.toolchain - fenix.packages.x86_64-linux.rust-analyzer + fenix.packages.x86_64-linux.complete.toolchain + #fenix.packages.x86_64-linux.rust-analyzer ]; shellHook = '' - echo "AEROGRAME DEVELOPMENT SHELL ${fenix.packages.x86_64-linux.minimal.rustc}" - export RUST_SRC_PATH="${fenix.packages.x86_64-linux.latest.rust-src}/lib/rustlib/src/rust/library" + echo "AEROGRAME DEVELOPMENT SHELL ${fenix.packages.x86_64-linux.complete.toolchain}" + export RUST_SRC_PATH="${fenix.packages.x86_64-linux.complete.toolchain}/lib/rustlib/src/rust/library" export RUST_ANALYZER_INTERNALS_DO_NOT_USE='this is unstable' ''; }; diff --git a/fuzz/fuzz_targets/dav.rs b/fuzz/fuzz_targets/dav.rs deleted file mode 100644 index 7549a03..0000000 --- a/fuzz/fuzz_targets/dav.rs +++ /dev/null @@ -1,48 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use aerogramme::dav::{types, realization, xml}; -use quick_xml::reader::NsReader; -use tokio::runtime::Runtime; -use tokio::io::AsyncWriteExt; - -async fn serialize(elem: &impl xml::QWrite) -> Vec { - let mut buffer = Vec::new(); - let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer); - let q = quick_xml::writer::Writer::new_with_indent(&mut tokio_buffer, b' ', 4); - let ns_to_apply = vec![ ("xmlns:D".into(), "DAV:".into()) ]; - let mut writer = xml::Writer { q, ns_to_apply }; - - elem.qwrite(&mut writer).await.expect("xml serialization"); - tokio_buffer.flush().await.expect("tokio buffer flush"); - - return buffer -} - -type Object = types::Multistatus>; - -fuzz_target!(|data: &[u8]| { - let rt = Runtime::new().expect("tokio runtime initialization"); - - rt.block_on(async { - // 1. Setup fuzzing by finding an input that seems correct, do not crash yet then. - let mut rdr = match xml::Reader::new(NsReader::from_reader(data)).await { - Err(_) => return, - Ok(r) => r, - }; - let reference = match rdr.find::().await { - Err(_) => return, - Ok(m) => m, - }; - - // 2. Re-serialize the input - let my_serialization = serialize(&reference).await; - - // 3. De-serialize my serialization - let mut rdr2 = xml::Reader::new(NsReader::from_reader(my_serialization.as_slice())).await.expect("XML Reader init"); - let comparison = rdr2.find::().await.expect("Deserialize again"); - - // 4. Both the first decoding and last decoding must be identical - assert_eq!(reference, comparison); - }) -}); diff --git a/src/auth.rs b/src/auth.rs deleted file mode 100644 index 064c90c..0000000 --- a/src/auth.rs +++ /dev/null @@ -1,941 +0,0 @@ -use std::net::SocketAddr; - -use anyhow::{anyhow, bail, Result}; -use futures::stream::{FuturesUnordered, StreamExt}; -use tokio::io::BufStream; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; -use tokio::net::{TcpListener, TcpStream}; -use tokio::sync::watch; - -use crate::config::AuthConfig; -use crate::login::ArcLoginProvider; - -/// Seek compatibility with the Dovecot Authentication Protocol -/// -/// ## Trace -/// -/// ```text -/// S: VERSION 1 2 -/// S: MECH PLAIN plaintext -/// S: MECH LOGIN plaintext -/// S: SPID 15 -/// S: CUID 17654 -/// S: COOKIE f56692bee41f471ed01bd83520025305 -/// S: DONE -/// C: VERSION 1 2 -/// C: CPID 1 -/// -/// C: AUTH 2 PLAIN service=smtp -/// S: CONT 2 -/// C: CONT 2 base64stringFollowingRFC4616== -/// S: OK 2 user=alice@example.tld -/// -/// C: AUTH 42 LOGIN service=smtp -/// S: CONT 42 VXNlcm5hbWU6 -/// C: CONT 42 b64User -/// S: CONT 42 UGFzc3dvcmQ6 -/// C: CONT 42 b64Pass -/// S: FAIL 42 user=alice -/// ``` -/// -/// ## RFC References -/// -/// PLAIN SASL - https://datatracker.ietf.org/doc/html/rfc4616 -/// -/// -/// ## Dovecot References -/// -/// https://doc.dovecot.org/developer_manual/design/auth_protocol/ -/// https://doc.dovecot.org/configuration_manual/authentication/authentication_mechanisms/#authentication-authentication-mechanisms -/// https://doc.dovecot.org/configuration_manual/howto/simple_virtual_install/#simple-virtual-install-smtp-auth -/// https://doc.dovecot.org/configuration_manual/howto/postfix_and_dovecot_sasl/#howto-postfix-and-dovecot-sasl -pub struct AuthServer { - login_provider: ArcLoginProvider, - bind_addr: SocketAddr, -} - -impl AuthServer { - pub fn new(config: AuthConfig, login_provider: ArcLoginProvider) -> Self { - Self { - bind_addr: config.bind_addr, - login_provider, - } - } - - pub async fn run(self: Self, mut must_exit: watch::Receiver) -> Result<()> { - let tcp = TcpListener::bind(self.bind_addr).await?; - tracing::info!( - "SASL Authentication Protocol listening on {:#}", - self.bind_addr - ); - - let mut connections = FuturesUnordered::new(); - - while !*must_exit.borrow() { - let wait_conn_finished = async { - if connections.is_empty() { - futures::future::pending().await - } else { - connections.next().await - } - }; - - let (socket, remote_addr) = tokio::select! { - a = tcp.accept() => a?, - _ = wait_conn_finished => continue, - _ = must_exit.changed() => continue, - }; - - tracing::info!("AUTH: accepted connection from {}", remote_addr); - let conn = tokio::spawn( - NetLoop::new(socket, self.login_provider.clone(), must_exit.clone()).run_error(), - ); - - connections.push(conn); - } - drop(tcp); - - tracing::info!("AUTH server shutting down, draining remaining connections..."); - while connections.next().await.is_some() {} - - Ok(()) - } -} - -struct NetLoop { - login: ArcLoginProvider, - stream: BufStream, - stop: watch::Receiver, - state: State, - read_buf: Vec, - write_buf: BytesMut, -} - -impl NetLoop { - fn new(stream: TcpStream, login: ArcLoginProvider, stop: watch::Receiver) -> Self { - Self { - login, - stream: BufStream::new(stream), - state: State::Init, - stop, - read_buf: Vec::new(), - write_buf: BytesMut::new(), - } - } - - async fn run_error(self) { - match self.run().await { - Ok(()) => tracing::info!("Auth session succeeded"), - Err(e) => tracing::error!(err=?e, "Auth session failed"), - } - } - - async fn run(mut self) -> Result<()> { - loop { - tokio::select! { - read_res = self.stream.read_until(b'\n', &mut self.read_buf) => { - // Detect EOF / socket close - let bread = read_res?; - if bread == 0 { - tracing::info!("Reading buffer empty, connection has been closed. Exiting AUTH session."); - return Ok(()) - } - - // Parse command - let (_, cmd) = client_command(&self.read_buf).map_err(|_| anyhow!("Unable to parse command"))?; - tracing::trace!(cmd=?cmd, "Received command"); - - // Make some progress in our local state - self.state.progress(cmd, &self.login).await; - if matches!(self.state, State::Error) { - bail!("Internal state is in error, previous logs explain what went wrong"); - } - - // Build response - let srv_cmds = self.state.response(); - srv_cmds.iter().try_for_each(|r| { - tracing::trace!(cmd=?r, "Sent command"); - r.encode(&mut self.write_buf) - })?; - - // Send responses if at least one command response has been generated - if !srv_cmds.is_empty() { - self.stream.write_all(&self.write_buf).await?; - self.stream.flush().await?; - } - - // Reset buffers - self.read_buf.clear(); - self.write_buf.clear(); - }, - _ = self.stop.changed() => { - tracing::debug!("Server is stopping, quitting this runner"); - return Ok(()) - } - } - } - } -} - -// ----------------------------------------------------------------- -// -// BUSINESS LOGIC -// -// ----------------------------------------------------------------- -use rand::prelude::*; - -#[derive(Debug)] -enum AuthRes { - Success(String), - Failed(Option, Option), -} - -#[derive(Debug)] -enum State { - Error, - Init, - HandshakePart(Version), - HandshakeDone, - AuthPlainProgress { id: u64 }, - AuthDone { id: u64, res: AuthRes }, -} - -const SERVER_MAJOR: u64 = 1; -const SERVER_MINOR: u64 = 2; -const EMPTY_AUTHZ: &[u8] = &[]; -impl State { - async fn try_auth_plain<'a>(&self, data: &'a [u8], login: &ArcLoginProvider) -> AuthRes { - // Check that we can extract user's login+pass - let (ubin, pbin) = match auth_plain(&data) { - Ok(([], (authz, user, pass))) if authz == user || authz == EMPTY_AUTHZ => (user, pass), - Ok(_) => { - tracing::error!("Impersonating user is not supported"); - return AuthRes::Failed(None, None); - } - Err(e) => { - tracing::error!(err=?e, "Could not parse the SASL PLAIN data chunk"); - return AuthRes::Failed(None, None); - } - }; - - // Try to convert it to UTF-8 - let (user, password) = match (std::str::from_utf8(ubin), std::str::from_utf8(pbin)) { - (Ok(u), Ok(p)) => (u, p), - _ => { - tracing::error!("Username or password contain invalid UTF-8 characters"); - return AuthRes::Failed(None, None); - } - }; - - // Try to connect user - match login.login(user, password).await { - Ok(_) => AuthRes::Success(user.to_string()), - Err(e) => { - tracing::warn!(err=?e, "login failed"); - AuthRes::Failed(Some(user.to_string()), None) - } - } - } - - async fn progress(&mut self, cmd: ClientCommand, login: &ArcLoginProvider) { - let new_state = 'state: { - match (std::mem::replace(self, State::Error), cmd) { - (Self::Init, ClientCommand::Version(v)) => Self::HandshakePart(v), - (Self::HandshakePart(version), ClientCommand::Cpid(_cpid)) => { - if version.major != SERVER_MAJOR { - tracing::error!( - client_major = version.major, - server_major = SERVER_MAJOR, - "Unsupported client major version" - ); - break 'state Self::Error; - } - - Self::HandshakeDone - } - ( - Self::HandshakeDone { .. }, - ClientCommand::Auth { - id, mech, options, .. - }, - ) - | ( - Self::AuthDone { .. }, - ClientCommand::Auth { - id, mech, options, .. - }, - ) => { - if mech != Mechanism::Plain { - tracing::error!(mechanism=?mech, "Unsupported Authentication Mechanism"); - break 'state Self::AuthDone { - id, - res: AuthRes::Failed(None, None), - }; - } - - match options.last() { - Some(AuthOption::Resp(data)) => Self::AuthDone { - id, - res: self.try_auth_plain(&data, login).await, - }, - _ => Self::AuthPlainProgress { id }, - } - } - (Self::AuthPlainProgress { id }, ClientCommand::Cont { id: cid, data }) => { - // Check that ID matches - if cid != id { - tracing::error!( - auth_id = id, - cont_id = cid, - "CONT id does not match AUTH id" - ); - break 'state Self::AuthDone { - id, - res: AuthRes::Failed(None, None), - }; - } - - Self::AuthDone { - id, - res: self.try_auth_plain(&data, login).await, - } - } - _ => { - tracing::error!("This command is not valid in this context"); - Self::Error - } - } - }; - tracing::debug!(state=?new_state, "Made progress"); - *self = new_state; - } - - fn response(&self) -> Vec { - let mut srv_cmd: Vec = Vec::new(); - - match self { - Self::HandshakeDone { .. } => { - srv_cmd.push(ServerCommand::Version(Version { - major: SERVER_MAJOR, - minor: SERVER_MINOR, - })); - - srv_cmd.push(ServerCommand::Mech { - kind: Mechanism::Plain, - parameters: vec![MechanismParameters::PlainText], - }); - - srv_cmd.push(ServerCommand::Spid(15u64)); - srv_cmd.push(ServerCommand::Cuid(19350u64)); - - let mut cookie = [0u8; 16]; - thread_rng().fill(&mut cookie); - srv_cmd.push(ServerCommand::Cookie(cookie)); - - srv_cmd.push(ServerCommand::Done); - } - Self::AuthPlainProgress { id } => { - srv_cmd.push(ServerCommand::Cont { - id: *id, - data: None, - }); - } - Self::AuthDone { - id, - res: AuthRes::Success(user), - } => { - srv_cmd.push(ServerCommand::Ok { - id: *id, - user_id: Some(user.to_string()), - extra_parameters: vec![], - }); - } - Self::AuthDone { - id, - res: AuthRes::Failed(maybe_user, maybe_failcode), - } => { - srv_cmd.push(ServerCommand::Fail { - id: *id, - user_id: maybe_user.clone(), - code: maybe_failcode.clone(), - extra_parameters: vec![], - }); - } - _ => (), - }; - - srv_cmd - } -} - -// ----------------------------------------------------------------- -// -// DOVECOT AUTH TYPES -// -// ----------------------------------------------------------------- - -#[derive(Debug, Clone, PartialEq)] -enum Mechanism { - Plain, - Login, -} - -#[derive(Clone, Debug)] -enum AuthOption { - /// Unique session ID. Mainly used for logging. - Session(u64), - /// Local IP connected to by the client. In standard string format, e.g. 127.0.0.1 or ::1. - LocalIp(String), - /// Remote client IP - RemoteIp(String), - /// Local port connected to by the client. - LocalPort(u16), - /// Remote client port - RemotePort(u16), - /// When Dovecot proxy is used, the real_rip/real_port are the proxy’s IP/port and real_lip/real_lport are the backend’s IP/port where the proxy was connected to. - RealRemoteIp(String), - RealLocalIp(String), - RealLocalPort(u16), - RealRemotePort(u16), - /// TLS SNI name - LocalName(String), - /// Enable debugging for this lookup. - Debug, - /// List of fields that will become available via %{forward_*} variables. The list is double-tab-escaped, like: tab_escaped[tab_escaped(key=value)[...] - /// Note: we do not unescape the tabulation, and thus we don't parse the data - ForwardViews(Vec), - /// Remote user has secured transport to auth client (e.g. localhost, SSL, TLS). - Secured(Option), - /// The value can be “insecure”, “trusted” or “TLS”. - Transport(String), - /// TLS cipher being used. - TlsCipher(String), - /// The number of bits in the TLS cipher. - /// @FIXME: I don't know how if it's a string or an integer - TlsCipherBits(String), - /// TLS perfect forward secrecy algorithm (e.g. DH, ECDH) - TlsPfs(String), - /// TLS protocol name (e.g. SSLv3, TLSv1.2) - TlsProtocol(String), - /// Remote user has presented a valid SSL certificate. - ValidClientCert(String), - /// Ignore auth penalty tracking for this request - NoPenalty, - /// Unknown option sent by Postfix - NoLogin, - /// Username taken from client’s SSL certificate. - CertUsername, - /// IMAP ID string - ClientId, - /// An unknown key - UnknownPair(String, Vec), - UnknownBool(Vec), - /// Initial response for authentication mechanism. - /// NOTE: This must be the last parameter. Everything after it is ignored. - /// This is to avoid accidental security holes if user-given data is directly put to base64 string without filtering out tabs. - /// @FIXME: I don't understand this parameter - Resp(Vec), -} - -#[derive(Debug, Clone)] -struct Version { - major: u64, - minor: u64, -} - -#[derive(Debug)] -enum ClientCommand { - /// Both client and server should check that they support the same major version number. If they don’t, the other side isn’t expected to be talking the same protocol and should be disconnected. Minor version can be ignored. This document specifies the version number 1.2. - Version(Version), - /// CPID finishes the handshake from client. - Cpid(u64), - Auth { - /// ID is a connection-specific unique request identifier. It must be a 32bit number, so typically you’d just increment it by one. - id: u64, - /// A SASL mechanism (eg. LOGIN, PLAIN, etc.) - /// See: https://doc.dovecot.org/configuration_manual/authentication/authentication_mechanisms/#authentication-authentication-mechanisms - mech: Mechanism, - /// Service is the service requesting authentication, eg. pop3, imap, smtp. - service: String, - /// All the optional parameters - options: Vec, - }, - Cont { - /// The must match the of the AUTH command. - id: u64, - /// Data that will be serialized to / deserialized from base64 - data: Vec, - }, -} - -#[derive(Debug)] -enum MechanismParameters { - /// Anonymous authentication - Anonymous, - /// Transfers plaintext passwords - PlainText, - /// Subject to passive (dictionary) attack - Dictionary, - /// Subject to active (non-dictionary) attack - Active, - /// Provides forward secrecy between sessions - ForwardSecrecy, - /// Provides mutual authentication - MutualAuth, - /// Don’t advertise this as available SASL mechanism (eg. APOP) - Private, -} - -#[derive(Debug, Clone)] -enum FailCode { - /// This is a temporary internal failure, e.g. connection was lost to SQL database. - TempFail, - /// Authentication succeeded, but authorization failed (master user’s password was ok, but destination user was not ok). - AuthzFail, - /// User is disabled (password may or may not have been correct) - UserDisabled, - /// User’s password has expired. - PassExpired, -} - -#[derive(Debug)] -enum ServerCommand { - /// Both client and server should check that they support the same major version number. If they don’t, the other side isn’t expected to be talking the same protocol and should be disconnected. Minor version can be ignored. This document specifies the version number 1.2. - Version(Version), - /// CPID and SPID specify client and server Process Identifiers (PIDs). They should be unique identifiers for the specific process. UNIX process IDs are good choices. - /// SPID can be used by authentication client to tell master which server process handled the authentication. - Spid(u64), - /// CUID is a server process-specific unique connection identifier. It’s different each time a connection is established for the server. - /// CUID is currently useful only for APOP authentication. - Cuid(u64), - Mech { - kind: Mechanism, - parameters: Vec, - }, - /// COOKIE returns connection-specific 128 bit cookie in hex. It must be given to REQUEST command. (Protocol v1.1+ / Dovecot v2.0+) - Cookie([u8; 16]), - /// DONE finishes the handshake from server. - Done, - - Fail { - id: u64, - user_id: Option, - code: Option, - extra_parameters: Vec>, - }, - Cont { - id: u64, - data: Option>, - }, - /// FAIL and OK may contain multiple unspecified parameters which authentication client may handle specially. - /// The only one specified here is user= parameter, which should always be sent if the userid is known. - Ok { - id: u64, - user_id: Option, - extra_parameters: Vec>, - }, -} - -// ----------------------------------------------------------------- -// -// DOVECOT AUTH DECODING -// -// ------------------------------------------------------------------ - -use base64::Engine; -use nom::{ - branch::alt, - bytes::complete::{is_not, tag, tag_no_case, take, take_while, take_while1}, - character::complete::{tab, u16, u64}, - combinator::{map, opt, recognize, rest, value}, - error::{Error, ErrorKind}, - multi::{many1, separated_list0}, - sequence::{pair, preceded, tuple}, - IResult, -}; - -fn version_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> { - let mut parser = tuple((tag_no_case(b"VERSION"), tab, u64, tab, u64)); - - let (input, (_, _, major, _, minor)) = parser(input)?; - Ok((input, ClientCommand::Version(Version { major, minor }))) -} - -fn cpid_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> { - preceded( - pair(tag_no_case(b"CPID"), tab), - map(u64, |v| ClientCommand::Cpid(v)), - )(input) -} - -fn mechanism<'a>(input: &'a [u8]) -> IResult<&'a [u8], Mechanism> { - alt(( - value(Mechanism::Plain, tag_no_case(b"PLAIN")), - value(Mechanism::Login, tag_no_case(b"LOGIN")), - ))(input) -} - -fn is_not_tab_or_esc_or_lf(c: u8) -> bool { - c != 0x09 && c != 0x01 && c != 0x0a // TAB or 0x01 or LF -} - -fn is_esc<'a>(input: &'a [u8]) -> IResult<&'a [u8], &[u8]> { - preceded(tag(&[0x01]), take(1usize))(input) -} - -fn parameter<'a>(input: &'a [u8]) -> IResult<&'a [u8], &[u8]> { - recognize(many1(alt((take_while1(is_not_tab_or_esc_or_lf), is_esc))))(input) -} - -fn parameter_str(input: &[u8]) -> IResult<&[u8], String> { - let (input, buf) = parameter(input)?; - - std::str::from_utf8(buf) - .map(|v| (input, v.to_string())) - .map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1))) -} - -fn is_param_name_char(c: u8) -> bool { - is_not_tab_or_esc_or_lf(c) && c != 0x3d // = -} - -fn parameter_name(input: &[u8]) -> IResult<&[u8], String> { - let (input, buf) = take_while1(is_param_name_char)(input)?; - - std::str::from_utf8(buf) - .map(|v| (input, v.to_string())) - .map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1))) -} - -fn service<'a>(input: &'a [u8]) -> IResult<&'a [u8], String> { - preceded(tag_no_case("service="), parameter_str)(input) -} - -fn auth_option<'a>(input: &'a [u8]) -> IResult<&'a [u8], AuthOption> { - use AuthOption::*; - alt(( - alt(( - value(Debug, tag_no_case(b"debug")), - value(NoPenalty, tag_no_case(b"no-penalty")), - value(ClientId, tag_no_case(b"client_id")), - value(NoLogin, tag_no_case(b"nologin")), - map(preceded(tag_no_case(b"session="), u64), |id| Session(id)), - map(preceded(tag_no_case(b"lip="), parameter_str), |ip| { - LocalIp(ip) - }), - map(preceded(tag_no_case(b"rip="), parameter_str), |ip| { - RemoteIp(ip) - }), - map(preceded(tag_no_case(b"lport="), u16), |port| { - LocalPort(port) - }), - map(preceded(tag_no_case(b"rport="), u16), |port| { - RemotePort(port) - }), - map(preceded(tag_no_case(b"real_rip="), parameter_str), |ip| { - RealRemoteIp(ip) - }), - map(preceded(tag_no_case(b"real_lip="), parameter_str), |ip| { - RealLocalIp(ip) - }), - map(preceded(tag_no_case(b"real_lport="), u16), |port| { - RealLocalPort(port) - }), - map(preceded(tag_no_case(b"real_rport="), u16), |port| { - RealRemotePort(port) - }), - )), - alt(( - map( - preceded(tag_no_case(b"local_name="), parameter_str), - |name| LocalName(name), - ), - map( - preceded(tag_no_case(b"forward_views="), parameter), - |views| ForwardViews(views.into()), - ), - map(preceded(tag_no_case(b"secured="), parameter_str), |info| { - Secured(Some(info)) - }), - value(Secured(None), tag_no_case(b"secured")), - value(CertUsername, tag_no_case(b"cert_username")), - map(preceded(tag_no_case(b"transport="), parameter_str), |ts| { - Transport(ts) - }), - map( - preceded(tag_no_case(b"tls_cipher="), parameter_str), - |cipher| TlsCipher(cipher), - ), - map( - preceded(tag_no_case(b"tls_cipher_bits="), parameter_str), - |bits| TlsCipherBits(bits), - ), - map(preceded(tag_no_case(b"tls_pfs="), parameter_str), |pfs| { - TlsPfs(pfs) - }), - map( - preceded(tag_no_case(b"tls_protocol="), parameter_str), - |proto| TlsProtocol(proto), - ), - map( - preceded(tag_no_case(b"valid-client-cert="), parameter_str), - |cert| ValidClientCert(cert), - ), - )), - alt(( - map(preceded(tag_no_case(b"resp="), base64), |data| Resp(data)), - map( - tuple((parameter_name, tag(b"="), parameter)), - |(n, _, v)| UnknownPair(n, v.into()), - ), - map(parameter, |v| UnknownBool(v.into())), - )), - ))(input) -} - -fn auth_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> { - let mut parser = tuple(( - tag_no_case(b"AUTH"), - tab, - u64, - tab, - mechanism, - tab, - service, - map(opt(preceded(tab, separated_list0(tab, auth_option))), |o| { - o.unwrap_or(vec![]) - }), - )); - let (input, (_, _, id, _, mech, _, service, options)) = parser(input)?; - Ok(( - input, - ClientCommand::Auth { - id, - mech, - service, - options, - }, - )) -} - -fn is_base64_core(c: u8) -> bool { - c >= 0x30 && c <= 0x39 // 0-9 - || c >= 0x41 && c <= 0x5a // A-Z - || c >= 0x61 && c <= 0x7a // a-z - || c == 0x2b // + - || c == 0x2f // / -} - -fn is_base64_pad(c: u8) -> bool { - c == 0x3d // = -} - -fn base64(input: &[u8]) -> IResult<&[u8], Vec> { - let (input, (b64, _)) = tuple((take_while1(is_base64_core), take_while(is_base64_pad)))(input)?; - - let data = base64::engine::general_purpose::STANDARD_NO_PAD - .decode(b64) - .map_err(|_| nom::Err::Failure(Error::new(input, ErrorKind::TakeWhile1)))?; - - Ok((input, data)) -} - -/// @FIXME Dovecot does not say if base64 content must be padded or not -fn cont_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> { - let mut parser = tuple((tag_no_case(b"CONT"), tab, u64, tab, base64)); - - let (input, (_, _, id, _, data)) = parser(input)?; - Ok((input, ClientCommand::Cont { id, data })) -} - -fn client_command<'a>(input: &'a [u8]) -> IResult<&'a [u8], ClientCommand> { - alt((version_command, cpid_command, auth_command, cont_command))(input) -} - -/* -fn server_command(buf: &u8) -> IResult<&u8, ServerCommand> { - unimplemented!(); -} -*/ - -// ----------------------------------------------------------------- -// -// SASL DECODING -// -// ----------------------------------------------------------------- - -fn not_null(c: u8) -> bool { - c != 0x0 -} - -// impersonated user, login, password -fn auth_plain<'a>(input: &'a [u8]) -> IResult<&'a [u8], (&'a [u8], &'a [u8], &'a [u8])> { - map( - tuple(( - take_while(not_null), - take(1usize), - take_while(not_null), - take(1usize), - rest, - )), - |(imp, _, user, _, pass)| (imp, user, pass), - )(input) -} - -// ----------------------------------------------------------------- -// -// DOVECOT AUTH ENCODING -// -// ------------------------------------------------------------------ -use tokio_util::bytes::{BufMut, BytesMut}; -trait Encode { - fn encode(&self, out: &mut BytesMut) -> Result<()>; -} - -fn tab_enc(out: &mut BytesMut) { - out.put(&[0x09][..]) -} - -fn lf_enc(out: &mut BytesMut) { - out.put(&[0x0A][..]) -} - -impl Encode for Mechanism { - fn encode(&self, out: &mut BytesMut) -> Result<()> { - match self { - Self::Plain => out.put(&b"PLAIN"[..]), - Self::Login => out.put(&b"LOGIN"[..]), - } - Ok(()) - } -} - -impl Encode for MechanismParameters { - fn encode(&self, out: &mut BytesMut) -> Result<()> { - match self { - Self::Anonymous => out.put(&b"anonymous"[..]), - Self::PlainText => out.put(&b"plaintext"[..]), - Self::Dictionary => out.put(&b"dictionary"[..]), - Self::Active => out.put(&b"active"[..]), - Self::ForwardSecrecy => out.put(&b"forward-secrecy"[..]), - Self::MutualAuth => out.put(&b"mutual-auth"[..]), - Self::Private => out.put(&b"private"[..]), - } - Ok(()) - } -} - -impl Encode for FailCode { - fn encode(&self, out: &mut BytesMut) -> Result<()> { - match self { - Self::TempFail => out.put(&b"temp_fail"[..]), - Self::AuthzFail => out.put(&b"authz_fail"[..]), - Self::UserDisabled => out.put(&b"user_disabled"[..]), - Self::PassExpired => out.put(&b"pass_expired"[..]), - }; - Ok(()) - } -} - -impl Encode for ServerCommand { - fn encode(&self, out: &mut BytesMut) -> Result<()> { - match self { - Self::Version(Version { major, minor }) => { - out.put(&b"VERSION"[..]); - tab_enc(out); - out.put(major.to_string().as_bytes()); - tab_enc(out); - out.put(minor.to_string().as_bytes()); - lf_enc(out); - } - Self::Spid(pid) => { - out.put(&b"SPID"[..]); - tab_enc(out); - out.put(pid.to_string().as_bytes()); - lf_enc(out); - } - Self::Cuid(pid) => { - out.put(&b"CUID"[..]); - tab_enc(out); - out.put(pid.to_string().as_bytes()); - lf_enc(out); - } - Self::Cookie(cval) => { - out.put(&b"COOKIE"[..]); - tab_enc(out); - out.put(hex::encode(cval).as_bytes()); - lf_enc(out); - } - Self::Mech { kind, parameters } => { - out.put(&b"MECH"[..]); - tab_enc(out); - kind.encode(out)?; - for p in parameters.iter() { - tab_enc(out); - p.encode(out)?; - } - lf_enc(out); - } - Self::Done => { - out.put(&b"DONE"[..]); - lf_enc(out); - } - Self::Cont { id, data } => { - out.put(&b"CONT"[..]); - tab_enc(out); - out.put(id.to_string().as_bytes()); - tab_enc(out); - if let Some(rdata) = data { - let b64 = base64::engine::general_purpose::STANDARD.encode(rdata); - out.put(b64.as_bytes()); - } - lf_enc(out); - } - Self::Ok { - id, - user_id, - extra_parameters, - } => { - out.put(&b"OK"[..]); - tab_enc(out); - out.put(id.to_string().as_bytes()); - if let Some(user) = user_id { - tab_enc(out); - out.put(&b"user="[..]); - out.put(user.as_bytes()); - } - for p in extra_parameters.iter() { - tab_enc(out); - out.put(&p[..]); - } - lf_enc(out); - } - Self::Fail { - id, - user_id, - code, - extra_parameters, - } => { - out.put(&b"FAIL"[..]); - tab_enc(out); - out.put(id.to_string().as_bytes()); - if let Some(user) = user_id { - tab_enc(out); - out.put(&b"user="[..]); - out.put(user.as_bytes()); - } - if let Some(code_val) = code { - tab_enc(out); - out.put(&b"code="[..]); - code_val.encode(out)?; - } - for p in extra_parameters.iter() { - tab_enc(out); - out.put(&p[..]); - } - lf_enc(out); - } - } - Ok(()) - } -}