diff options
-rw-r--r-- | Cargo.lock | 1755 | ||||
-rw-r--r-- | Cargo.toml | 41 | ||||
-rw-r--r-- | aero-bayou/Cargo.toml | 17 | ||||
-rw-r--r-- | aero-bayou/src/lib.rs (renamed from src/bayou.rs) | 9 | ||||
-rw-r--r-- | aero-bayou/src/timestamp.rs (renamed from src/timestamp.rs) | 3 | ||||
-rw-r--r-- | aero-collections/mail/incoming.rs (renamed from src/mail/incoming.rs) | 0 | ||||
-rw-r--r-- | aero-collections/mail/mailbox.rs (renamed from src/mail/mailbox.rs) | 0 | ||||
-rw-r--r-- | aero-collections/mail/mod.rs (renamed from src/mail/mod.rs) | 0 | ||||
-rw-r--r-- | aero-collections/mail/namespace.rs (renamed from src/mail/namespace.rs) | 0 | ||||
-rw-r--r-- | aero-collections/mail/query.rs (renamed from src/mail/query.rs) | 0 | ||||
-rw-r--r-- | aero-collections/mail/snapshot.rs (renamed from src/mail/snapshot.rs) | 0 | ||||
-rw-r--r-- | aero-collections/mail/uidindex.rs (renamed from src/mail/uidindex.rs) | 0 | ||||
-rw-r--r-- | aero-collections/mail/unique_ident.rs (renamed from src/mail/unique_ident.rs) | 0 | ||||
-rw-r--r-- | aero-collections/user.rs (renamed from src/user.rs) | 0 | ||||
-rw-r--r-- | aero-dav/.gitignore | 1 | ||||
-rw-r--r-- | aero-dav/Cargo.toml | 14 | ||||
-rw-r--r-- | aero-dav/fuzz/.gitignore (renamed from fuzz/.gitignore) | 0 | ||||
-rw-r--r-- | aero-dav/fuzz/Cargo.lock (renamed from fuzz/Cargo.lock) | 0 | ||||
-rw-r--r-- | aero-dav/fuzz/Cargo.toml (renamed from fuzz/Cargo.toml) | 9 | ||||
-rw-r--r-- | aero-dav/fuzz/dav.dict | 126 | ||||
-rw-r--r-- | aero-dav/fuzz/fuzz_targets/dav.rs | 196 | ||||
-rw-r--r-- | aero-dav/src/acltypes.rs (renamed from src/dav/acltypes.rs) | 0 | ||||
-rw-r--r-- | aero-dav/src/caldecoder.rs (renamed from src/dav/caldecoder.rs) | 0 | ||||
-rw-r--r-- | aero-dav/src/calencoder.rs (renamed from src/dav/calencoder.rs) | 4 | ||||
-rw-r--r-- | aero-dav/src/caltypes.rs (renamed from src/dav/caltypes.rs) | 25 | ||||
-rw-r--r-- | aero-dav/src/decoder.rs (renamed from src/dav/decoder.rs) | 5 | ||||
-rw-r--r-- | aero-dav/src/encoder.rs (renamed from src/dav/encoder.rs) | 9 | ||||
-rw-r--r-- | aero-dav/src/error.rs (renamed from src/dav/error.rs) | 0 | ||||
-rw-r--r-- | aero-dav/src/lib.rs | 25 | ||||
-rw-r--r-- | aero-dav/src/realization.rs (renamed from src/dav/realization.rs) | 0 | ||||
-rw-r--r-- | aero-dav/src/types.rs (renamed from src/dav/types.rs) | 1 | ||||
-rw-r--r-- | aero-dav/src/versioningtypes.rs (renamed from src/dav/versioningtypes.rs) | 0 | ||||
-rw-r--r-- | aero-dav/src/xml.rs (renamed from src/dav/xml.rs) | 11 | ||||
-rw-r--r-- | aero-proto/dav.rs (renamed from src/dav/mod.rs) | 22 | ||||
-rw-r--r-- | aero-proto/imap/attributes.rs (renamed from src/imap/attributes.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/capability.rs (renamed from src/imap/capability.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/command/anonymous.rs (renamed from src/imap/command/anonymous.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/command/anystate.rs (renamed from src/imap/command/anystate.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/command/authenticated.rs (renamed from src/imap/command/authenticated.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/command/mod.rs (renamed from src/imap/command/mod.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/command/selected.rs (renamed from src/imap/command/selected.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/flags.rs (renamed from src/imap/flags.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/flow.rs (renamed from src/imap/flow.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/imf_view.rs (renamed from src/imap/imf_view.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/index.rs (renamed from src/imap/index.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/mail_view.rs (renamed from src/imap/mail_view.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/mailbox_view.rs (renamed from src/imap/mailbox_view.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/mime_view.rs (renamed from src/imap/mime_view.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/mod.rs (renamed from src/imap/mod.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/request.rs (renamed from src/imap/request.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/response.rs (renamed from src/imap/response.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/search.rs (renamed from src/imap/search.rs) | 0 | ||||
-rw-r--r-- | aero-proto/imap/session.rs (renamed from src/imap/session.rs) | 0 | ||||
-rw-r--r-- | aero-proto/lmtp.rs (renamed from src/lmtp.rs) | 0 | ||||
-rw-r--r-- | aero-proto/sasl.rs | 140 | ||||
-rw-r--r-- | aero-sasl/Cargo.toml | 22 | ||||
-rw-r--r-- | aero-sasl/src/decode.rs | 243 | ||||
-rw-r--r-- | aero-sasl/src/encode.rs | 157 | ||||
-rw-r--r-- | aero-sasl/src/flow.rs | 201 | ||||
-rw-r--r-- | aero-sasl/src/lib.rs | 43 | ||||
-rw-r--r-- | aero-sasl/src/types.rs | 163 | ||||
-rw-r--r-- | aero-user/Cargo.toml | 30 | ||||
-rw-r--r-- | aero-user/src/config.rs (renamed from src/config.rs) | 0 | ||||
-rw-r--r-- | aero-user/src/cryptoblob.rs (renamed from src/cryptoblob.rs) | 0 | ||||
-rw-r--r-- | aero-user/src/lib.rs | 9 | ||||
-rw-r--r-- | aero-user/src/login/demo_provider.rs (renamed from src/login/demo_provider.rs) | 0 | ||||
-rw-r--r-- | aero-user/src/login/ldap_provider.rs (renamed from src/login/ldap_provider.rs) | 3 | ||||
-rw-r--r-- | aero-user/src/login/mod.rs (renamed from src/login/mod.rs) | 2 | ||||
-rw-r--r-- | aero-user/src/login/static_provider.rs (renamed from src/login/static_provider.rs) | 7 | ||||
-rw-r--r-- | aero-user/src/storage/garage.rs (renamed from src/storage/garage.rs) | 2 | ||||
-rw-r--r-- | aero-user/src/storage/in_memory.rs (renamed from src/storage/in_memory.rs) | 4 | ||||
-rw-r--r-- | aero-user/src/storage/mod.rs (renamed from src/storage/mod.rs) | 3 | ||||
-rw-r--r-- | aerogramme/Cargo.toml | 12 | ||||
-rw-r--r-- | aerogramme/src/k2v_util.rs (renamed from src/k2v_util.rs) | 0 | ||||
-rw-r--r-- | aerogramme/src/lib.rs (renamed from src/lib.rs) | 0 | ||||
-rw-r--r-- | aerogramme/src/main.rs (renamed from src/main.rs) | 0 | ||||
-rw-r--r-- | aerogramme/src/server.rs (renamed from src/server.rs) | 0 | ||||
-rw-r--r-- | flake.nix | 8 | ||||
-rw-r--r-- | fuzz/fuzz_targets/dav.rs | 48 | ||||
-rw-r--r-- | src/auth.rs | 941 |
80 files changed, 1665 insertions, 2646 deletions
@@ -3,15 +3,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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,85 +66,53 @@ 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", + "aero-dav", + "arbitrary", + "libfuzzer-sys", + "quick-xml", + "tokio", ] [[package]] -name = "android-tzdata" -version = "0.1.1" +name = "anyhow" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] -name = "android_system_properties" -version = "0.1.5" +name = "arbitrary" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" dependencies = [ - "libc", + "derive_arbitrary", ] [[package]] -name = "anyhow" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" - -[[package]] name = "argon2" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ba4cac0a46bc1d2912652a751c47f2a9f3a7fe89bcae2275d418f5270402f9" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" dependencies = [ "base64ct", "blake2", @@ -124,12 +121,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -169,230 +160,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -404,57 +171,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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,65 +548,20 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -946,33 +617,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -991,42 +635,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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,29 +729,14 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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]] @@ -1338,16 +841,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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" @@ -1395,90 +888,12 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1495,16 +910,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1520,12 +925,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1574,34 +973,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1637,7 +1008,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.13", + "pin-project-lite", "pin-utils", "slab", ] @@ -1670,18 +1041,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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,8 +1081,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 1.0.0", - "indexmap 2.1.0", + "http 1.1.0", + "indexmap", "slab", "tokio", "tokio-util", @@ -1732,45 +1091,11 @@ dependencies = [ [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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]] @@ -1860,12 +1185,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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", @@ -1946,18 +1265,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1966,11 +1273,11 @@ 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", @@ -1978,40 +1285,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2022,106 +1295,13 @@ dependencies = [ ] [[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" -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" +version = "2.2.5" 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", @@ -2172,15 +1352,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2225,25 +1396,23 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2256,46 +1425,10 @@ dependencies = [ ] [[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" @@ -2314,12 +1447,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2346,17 +1473,6 @@ dependencies = [ ] [[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" @@ -2364,19 +1480,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" @@ -2386,16 +1489,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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", ] @@ -2466,24 +1559,12 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2495,12 +1576,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2525,18 +1600,18 @@ 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", @@ -2545,12 +1620,6 @@ dependencies = [ [[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" @@ -2562,17 +1631,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2589,36 +1647,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2631,30 +1659,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2664,38 +1668,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2715,12 +1687,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2751,65 +1717,12 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2872,27 +1785,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2917,33 +1809,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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,9 +1908,9 @@ 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", @@ -3053,16 +1918,10 @@ dependencies = [ ] [[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", @@ -3189,15 +2048,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3217,16 +2067,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3242,71 +2082,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3351,18 +2126,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3391,12 +2154,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3409,27 +2166,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3450,16 +2186,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3514,25 +2240,14 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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", ] @@ -3611,33 +2326,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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", ] @@ -3806,18 +2460,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3830,12 +2472,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3886,18 +2522,6 @@ dependencies = [ ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3978,15 +2602,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4119,12 +2734,6 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -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" @@ -1,16 +1,28 @@ -[package] -name = "aerogramme" -version = "0.3.0" -authors = ["Alex Auvolat <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"] -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 <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"] +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 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 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 index e2ad97d..e2ad97d 100644 --- a/src/mail/incoming.rs +++ b/aero-collections/mail/incoming.rs diff --git a/src/mail/mailbox.rs b/aero-collections/mail/mailbox.rs index d1a5473..d1a5473 100644 --- a/src/mail/mailbox.rs +++ b/aero-collections/mail/mailbox.rs diff --git a/src/mail/mod.rs b/aero-collections/mail/mod.rs index 03e85cd..03e85cd 100644 --- a/src/mail/mod.rs +++ b/aero-collections/mail/mod.rs diff --git a/src/mail/namespace.rs b/aero-collections/mail/namespace.rs index 5e67173..5e67173 100644 --- a/src/mail/namespace.rs +++ b/aero-collections/mail/namespace.rs diff --git a/src/mail/query.rs b/aero-collections/mail/query.rs index 3e6fe99..3e6fe99 100644 --- a/src/mail/query.rs +++ b/aero-collections/mail/query.rs diff --git a/src/mail/snapshot.rs b/aero-collections/mail/snapshot.rs index ed756b5..ed756b5 100644 --- a/src/mail/snapshot.rs +++ b/aero-collections/mail/snapshot.rs diff --git a/src/mail/uidindex.rs b/aero-collections/mail/uidindex.rs index 5a06670..5a06670 100644 --- a/src/mail/uidindex.rs +++ b/aero-collections/mail/uidindex.rs diff --git a/src/mail/unique_ident.rs b/aero-collections/mail/unique_ident.rs index 0e629db..0e629db 100644 --- a/src/mail/unique_ident.rs +++ b/aero-collections/mail/unique_ident.rs diff --git a/src/user.rs b/aero-collections/user.rs index a38b9c1..a38b9c1 100644 --- a/src/user.rs +++ b/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 <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"] +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 index 1a45eee..1a45eee 100644 --- a/fuzz/.gitignore +++ b/aero-dav/fuzz/.gitignore diff --git a/fuzz/Cargo.lock b/aero-dav/fuzz/Cargo.lock index 08fa951..08fa951 100644 --- a/fuzz/Cargo.lock +++ b/aero-dav/fuzz/Cargo.lock diff --git a/fuzz/Cargo.toml b/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="<!ATTLIST" +tag_cdata="<![CDATA[" +tag_close="</a>" +tag_doctype="<!DOCTYPE" +tag_element="<!ELEMENT" +tag_entity="<!ENTITY" +tag_ignore="<![IGNORE[" +tag_include="<![INCLUDE[" +tag_notation="<!NOTATION" +tag_open="<a>" +tag_open_close="<a />" +tag_open_exclamation="<!" +tag_open_q="<?" +tag_sq2_close="]]>" +tag_xml_q="<?xml?>" + +"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<Token>, + 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<Self>), + 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::<String>(), tag.end()), + } + }, + Self::Number(v) => format!("{}", v), + Self::Text(v) => v.serialize(), + } + } +} + +async fn serialize(elem: &impl xml::QWrite) -> Vec<u8> { + 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<realization::Core, types::PropValue<realization::Core>>; + +fuzz_target!(|nodes: XmlNode| { + let gen = format!("<D:multistatus xmlns:D=\"DAV:\">{}<D:/multistatus>", 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::<Object>().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::<Object>().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 index f356813..f356813 100644 --- a/src/dav/acltypes.rs +++ b/aero-dav/src/acltypes.rs diff --git a/src/dav/caldecoder.rs b/aero-dav/src/caldecoder.rs index 5f40c4b..5f40c4b 100644 --- a/src/dav/caldecoder.rs +++ b/aero-dav/src/caldecoder.rs diff --git a/src/dav/calencoder.rs b/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 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 /// <!ELEMENT mkcalendar (DAV:set)> +/// ``` #[derive(Debug, PartialEq)] pub struct MkCalendar<E: dav::Extension>(pub dav::Set<E>); @@ -197,12 +199,15 @@ pub enum Property { /// sequence "]]>", which is the end delimiter for the CDATA section. /// /// Definition: - /// + /// + /// ```xmlschema /// <!ELEMENT calendar-timezone (#PCDATA)> /// PCDATA value: an iCalendar object with exactly one VTIMEZONE component. + /// ``` /// /// Example: /// + /// ```xmlschema /// <C:calendar-timezone /// xmlns:C="urn:ietf:params:xml:ns:caldav">BEGIN:VCALENDAR /// PRODID:-//Example Corp.//CalDAV Client//EN @@ -227,6 +232,7 @@ pub enum Property { /// END:VTIMEZONE /// END:VCALENDAR /// </C:calendar-timezone> + /// ``` //@FIXME we might want to put a buffer here or an iCal parsed object CalendarTimezone(String), @@ -1123,12 +1129,15 @@ pub enum CalendarSelector<E: dav::Extension> { /// the targeted calendar component. /// /// Definition: +/// +/// ```xmlschema /// <!ELEMENT comp-filter (is-not-defined | (time-range?, /// prop-filter*, comp-filter*))> /// /// <!ATTLIST comp-filter name CDATA #REQUIRED> /// 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: /// -/// <!ELEMENT prop-filter (is-not-defined | -/// ((time-range | text-match)?, -/// param-filter*))> +/// ```xmlschema +/// <!ELEMENT prop-filter (is-not-defined | +/// ((time-range | text-match)?, +/// param-filter*))> /// -/// <!ATTLIST prop-filter name CDATA #REQUIRED> -/// name value: a calendar property name (e.g., ATTENDEE) +/// <!ATTLIST prop-filter name CDATA #REQUIRED> +/// 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 /// <!ELEMENT param-filter (is-not-defined | text-match?)> /// /// <!ATTLIST param-filter name CDATA #REQUIRED> /// 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 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<Href> 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 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<E: Extension> QWrite for Violation<E> { #[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 index 78c6d6b..78c6d6b 100644 --- a/src/dav/error.rs +++ b/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 index 33a556e..33a556e 100644 --- a/src/dav/realization.rs +++ b/aero-dav/src/realization.rs diff --git a/src/dav/types.rs b/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 index 6c1c204..6c1c204 100644 --- a/src/dav/versioningtypes.rs +++ b/aero-dav/src/versioningtypes.rs diff --git a/src/dav/xml.rs b/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<impl IWrite>) -> Result<(), quick_xml::Error>; + fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> impl Future<Output = Result<(), quick_xml::Error>>; } pub trait QRead<T> { - async fn qread(xml: &mut Reader<impl IRead>) -> Result<T, ParsingError>; + fn qread(xml: &mut Reader<impl IRead>) -> impl Future<Output = Result<T, ParsingError>>; } // The representation of an XML node in Rust diff --git a/src/dav/mod.rs b/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 index 89446a8..89446a8 100644 --- a/src/imap/attributes.rs +++ b/aero-proto/imap/attributes.rs diff --git a/src/imap/capability.rs b/aero-proto/imap/capability.rs index c76b51c..c76b51c 100644 --- a/src/imap/capability.rs +++ b/aero-proto/imap/capability.rs diff --git a/src/imap/command/anonymous.rs b/aero-proto/imap/command/anonymous.rs index 811d1e4..811d1e4 100644 --- a/src/imap/command/anonymous.rs +++ b/aero-proto/imap/command/anonymous.rs diff --git a/src/imap/command/anystate.rs b/aero-proto/imap/command/anystate.rs index 718ba3f..718ba3f 100644 --- a/src/imap/command/anystate.rs +++ b/aero-proto/imap/command/anystate.rs diff --git a/src/imap/command/authenticated.rs b/aero-proto/imap/command/authenticated.rs index 3d332ec..3d332ec 100644 --- a/src/imap/command/authenticated.rs +++ b/aero-proto/imap/command/authenticated.rs diff --git a/src/imap/command/mod.rs b/aero-proto/imap/command/mod.rs index f201eb6..f201eb6 100644 --- a/src/imap/command/mod.rs +++ b/aero-proto/imap/command/mod.rs diff --git a/src/imap/command/selected.rs b/aero-proto/imap/command/selected.rs index eedfbd6..eedfbd6 100644 --- a/src/imap/command/selected.rs +++ b/aero-proto/imap/command/selected.rs diff --git a/src/imap/flags.rs b/aero-proto/imap/flags.rs index 0f6ec64..0f6ec64 100644 --- a/src/imap/flags.rs +++ b/aero-proto/imap/flags.rs diff --git a/src/imap/flow.rs b/aero-proto/imap/flow.rs index 86eb12e..86eb12e 100644 --- a/src/imap/flow.rs +++ b/aero-proto/imap/flow.rs diff --git a/src/imap/imf_view.rs b/aero-proto/imap/imf_view.rs index a4ca2e8..a4ca2e8 100644 --- a/src/imap/imf_view.rs +++ b/aero-proto/imap/imf_view.rs diff --git a/src/imap/index.rs b/aero-proto/imap/index.rs index 9b794b8..9b794b8 100644 --- a/src/imap/index.rs +++ b/aero-proto/imap/index.rs diff --git a/src/imap/mail_view.rs b/aero-proto/imap/mail_view.rs index a8db733..a8db733 100644 --- a/src/imap/mail_view.rs +++ b/aero-proto/imap/mail_view.rs diff --git a/src/imap/mailbox_view.rs b/aero-proto/imap/mailbox_view.rs index 1c53b93..1c53b93 100644 --- a/src/imap/mailbox_view.rs +++ b/aero-proto/imap/mailbox_view.rs diff --git a/src/imap/mime_view.rs b/aero-proto/imap/mime_view.rs index 8bbbd2d..8bbbd2d 100644 --- a/src/imap/mime_view.rs +++ b/aero-proto/imap/mime_view.rs diff --git a/src/imap/mod.rs b/aero-proto/imap/mod.rs index 02ab9ce..02ab9ce 100644 --- a/src/imap/mod.rs +++ b/aero-proto/imap/mod.rs diff --git a/src/imap/request.rs b/aero-proto/imap/request.rs index cff18a3..cff18a3 100644 --- a/src/imap/request.rs +++ b/aero-proto/imap/request.rs diff --git a/src/imap/response.rs b/aero-proto/imap/response.rs index b6a0e98..b6a0e98 100644 --- a/src/imap/response.rs +++ b/aero-proto/imap/response.rs diff --git a/src/imap/search.rs b/aero-proto/imap/search.rs index 37a7e9e..37a7e9e 100644 --- a/src/imap/search.rs +++ b/aero-proto/imap/search.rs diff --git a/src/imap/session.rs b/aero-proto/imap/session.rs index fa3232a..fa3232a 100644 --- a/src/imap/session.rs +++ b/aero-proto/imap/session.rs diff --git a/src/lmtp.rs b/aero-proto/lmtp.rs index dcd4bcc..dcd4bcc 100644 --- a/src/lmtp.rs +++ b/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<bool>) -> 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<TcpStream>, + stop: watch::Receiver<bool>, + state: State, + read_buf: Vec<u8>, + write_buf: BytesMut, +} + +impl NetLoop { + fn new(stream: TcpStream, login: ArcLoginProvider, stop: watch::Receiver<bool>) -> 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 <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"] +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<u8>> { + 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<String>, Option<FailCode>), +} + +#[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<Output=bool>, + { + // 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<F,X>(&mut self, cmd: ClientCommand, login: X) + where + X: FnOnce(&str, &str) -> F, + F: Future<Output=bool>, + { + 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<ServerCommand> { + let mut srv_cmd: Vec<ServerCommand> = 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)[<TAB>...] + /// Note: we do not unescape the tabulation, and thus we don't parse the data + ForwardViews(Vec<u8>), + /// Remote user has secured transport to auth client (e.g. localhost, SSL, TLS). + Secured(Option<String>), + /// 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<u8>), + UnknownBool(Vec<u8>), + /// 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<u8>), +} + +#[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<AuthOption>, + }, + Cont { + /// The <id> must match the <id> of the AUTH command. + id: u64, + /// Data that will be serialized to / deserialized from base64 + data: Vec<u8>, + }, +} + +#[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<MechanismParameters>, + }, + /// 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<String>, + code: Option<FailCode>, + extra_parameters: Vec<Vec<u8>>, + }, + Cont { + id: u64, + data: Option<Vec<u8>>, + }, + /// FAIL and OK may contain multiple unspecified parameters which authentication client may handle specially. + /// The only one specified here is user=<userid> parameter, which should always be sent if the userid is known. + Ok { + id: u64, + user_id: Option<String>, + extra_parameters: Vec<Vec<u8>>, + }, +} + + 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 <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"] +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 index 7de2eac..7de2eac 100644 --- a/src/config.rs +++ b/aero-user/src/config.rs diff --git a/src/cryptoblob.rs b/aero-user/src/cryptoblob.rs index 327a642..327a642 100644 --- a/src/cryptoblob.rs +++ b/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 index 11c7d54..11c7d54 100644 --- a/src/login/demo_provider.rs +++ b/aero-user/src/login/demo_provider.rs diff --git a/src/login/ldap_provider.rs b/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 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 index 79626df..ed39343 100644 --- a/src/login/static_provider.rs +++ b/aero-user/src/login/static_provider.rs @@ -1,11 +1,10 @@ use std::collections::HashMap; use std::path::PathBuf; -use std::sync::Arc; -use tokio::signal::unix::{signal, SignalKind}; -use tokio::sync::watch; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail}; use async_trait::async_trait; +use tokio::signal::unix::{signal, SignalKind}; +use tokio::sync::watch; use crate::config::*; use crate::login::*; diff --git a/src/storage/garage.rs b/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<HttpsConnector<HttpConnector>, k2v_client::Body>, diff --git a/src/storage/in_memory.rs b/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 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 <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"] +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 index 3cd969b..3cd969b 100644 --- a/src/k2v_util.rs +++ b/aerogramme/src/k2v_util.rs diff --git a/src/lib.rs b/aerogramme/src/lib.rs index f065478..f065478 100644 --- a/src/lib.rs +++ b/aerogramme/src/lib.rs diff --git a/src/main.rs b/aerogramme/src/main.rs index 43b4dca..43b4dca 100644 --- a/src/main.rs +++ b/aerogramme/src/main.rs diff --git a/src/server.rs b/aerogramme/src/server.rs index 09e91ad..09e91ad 100644 --- a/src/server.rs +++ b/aerogramme/src/server.rs @@ -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<u8> { - 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<realization::Core, types::PropValue<realization::Core>>; - -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::<Object>().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::<Object>().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<bool>) -> 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<TcpStream>, - stop: watch::Receiver<bool>, - state: State, - read_buf: Vec<u8>, - write_buf: BytesMut, -} - -impl NetLoop { - fn new(stream: TcpStream, login: ArcLoginProvider, stop: watch::Receiver<bool>) -> 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<String>, Option<FailCode>), -} - -#[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<ServerCommand> { - let mut srv_cmd: Vec<ServerCommand> = 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)[<TAB>...] - /// Note: we do not unescape the tabulation, and thus we don't parse the data - ForwardViews(Vec<u8>), - /// Remote user has secured transport to auth client (e.g. localhost, SSL, TLS). - Secured(Option<String>), - /// 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<u8>), - UnknownBool(Vec<u8>), - /// 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<u8>), -} - -#[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<AuthOption>, - }, - Cont { - /// The <id> must match the <id> of the AUTH command. - id: u64, - /// Data that will be serialized to / deserialized from base64 - data: Vec<u8>, - }, -} - -#[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<MechanismParameters>, - }, - /// 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<String>, - code: Option<FailCode>, - extra_parameters: Vec<Vec<u8>>, - }, - Cont { - id: u64, - data: Option<Vec<u8>>, - }, - /// FAIL and OK may contain multiple unspecified parameters which authentication client may handle specially. - /// The only one specified here is user=<userid> parameter, which should always be sent if the userid is known. - Ok { - id: u64, - user_id: Option<String>, - extra_parameters: Vec<Vec<u8>>, - }, -} - -// ----------------------------------------------------------------- -// -// 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<u8>> { - 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(()) - } -} |