1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
//! `rsasl` is the Rust SASL framework designed to make supporting SASL in protocols and doing SASL
//! authentication in application code simple and safe.
//!
//! # Important note for users of this crate:
//!
//! Please do check out the documentation for [CHANGELOG](docs::changelog). The module
//! documentation is a render of the `CHANGELOG.md` distributed with rsasl. It contains
//! information about added features, bugfixes and other code changes in the different released
//! versions.
//!
//! Reading the changelog can help you decide the minimum version of rsasl you need to depend on
//! and will inform you of any bugfixes that may introduce backwards-incompatible changes.
//!
//! # SASL primer
//!
//! *you can safely skip this section if you know your way around SASL and just want to know
//! [how to use this library](#where-to-start).*
//!
//! [SASL (RFC 4422)](https://tools.ietf.org/html/rfc4422) was designed to separate
//! authentication from the remaining protocol implementation, both server- and client-side. The
//! goal was to make authentication pluggable and more modular, so that a new protocol doesn't
//! have to re-invent authentication from scratch and could instead rely on existing
//! implementations of just the authentication part.
//!
//! SASL implements this by abstracting all authentication into so called 'mechanisms' that
//! authenticate in a number of 'steps', with each step consisting of some amount of data being
//! sent from the client to the server and from the server to the client.
//! This data is explicitly opaque to the outer protocol (e.g. SMTP, IMAP, …). The protocol only
//! needs to define a way to transport this data to the respective other end. The way this is done
//! differs between protocols, but the format of the authentication data is always the same for any
//! given mechanism, so any mechanism can work with any protocol out of the box.
//!
//! One of the best known mechanism for example, `PLAIN`, always transports the username and
//! password separated by singular NULL bytes. Yet the very same plain authentication using the
//! username "username" and password "secret" looks very different in different protocols:
//!
//! IMAP:
//! ```text
//! S: * OK IMAP4rev1 Service Ready
//! C: D0 CAPABILITY
//! S: * CAPABILITY IMAP4 IMAP4rev1 AUTH=GSSAPI AUTH=PLAIN
//! S: D0 OK CAPABILITY completed.
//! C: D1 AUTHENTICATE PLAIN
//! S: +
//! C: AHVzZXJuYW1lAHNlY3JldAo=
//! S: D1 OK AUTHENTICATE completed
//! ```
//!
//! SMTP:
//! ```test
//! S: 220 smtp.example.com Simple Mail Transfer Service Ready
//! C: EHLO client.example.org
//! S: 250-smtp.server.com Hello client.example.org
//! S: 250-SIZE 1000000
//! S: 250 AUTH GSSAPI PLAIN
//! C: AUTH PLAIN
//! S: 334
//! C: AHVzZXJuYW1lAHNlY3JldAo=
//! S: 235 2.7.0 Authentication successful
//! ```
//!
//! XMPP:
//! ```text
//! S: <stream:features>
//! S:     <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
//! S:         <mechanism>GSSAPI</mechanism>
//! S:         <mechanism>PLAIN</mechanism>
//! S:     </mechanisms>
//! S: </stream:features>
//! C: <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>
//! C:     AHVzZXJuYW1lAHNlY3JldAo=
//! C: </auth>
//! ```
//!
//! But the inner authentication **data** (here base64 encoded as `AHVzZXJuYW1lAHNlY3JldAo=`)
//! is always the same.
//!
//! This modularity becomes an even bigger advantage when combined with cryptographically strong
//! authentication (like SCRAM) or single-sign-on technologies (like OpenID Connect or Kerberos).
//! Instead of every protocol and their implementations having to juggle cryptographic proofs or
//! figure out the latest SSO mechanism by themselves they can share their implementations.
//!
//! Of course a client or server application for those protocols still has to worry about
//! authentication and authorization on some level. Which is why rsasl is designed to make
//! authentication pluggable and enable middleware-style protocol crates that are entirely
//! authentication-agnostic, deferring the details entirely to their downstream users.
//!
//! # Where to start
//!
//! There are three different use-cases rsasl provides for:
//!
//! - [I'm implementing a client or server for a protocol and I need to support SASL authentication](#protocol-implementations)
//! - [I'm using a such a client or server and I need to configure my credentials](#application-code)
//! - [I need to implement a custom SASL mechanism](#custom-mechanisms)
//!
//!
//! # Protocol Implementations
//!
//! The starting point of rsasl for protocol implementations is the
//! [`SASLConfig`](prelude::SASLConfig) struct.
//! This struct is created by the downstream user of the protocol crate and contains all
//! required configuration and data to select mechanism and authenticate using them in an opaque
//! and storable way.
//! The `SASLConfig` type is designed to be long-lived and to be valid for multiple contexts and
//! authentication exchanges.
//!
//! To start an authentication a [`SASLClient`](prelude::SASLClient) or
//! [`SASLServer`](prelude::SASLServer) is constructed from this config, allowing a
//! protocol crate to provide additional, context-specific, data.
//!
//! The produced `SASLClient` / `SASLServer` are then of course also context-specific and usually
//! not readily reusable, for example channel bindings are specific to a single TLS session.
//! Thus a new `SASLClient` or `SASLServer` must be constructed for every connection.
//!
//! To finally start the authentication exchange itself a [`Session`](session::Session) is
//! constructed by having the `SASLClient` or `SASLServer` select the best mechanism using the
//! [`SASLClient::start_suggested`](prelude::SASLClient::start_suggested) or
//! [`SASLServer::start_suggested`](prelude::SASLServer::start_suggested) methods respectively.
//!
//! On the resulting session the methods [`Session::step`](session::Session::step) or
//! [`Session::step64`](session::Session::step64) are called until
//! [`State::Finished`](session::State::Finished) is returned:
//!
//! ```rust
//! # use std::io;
//! # use std::sync::Arc;
//! use rsasl::prelude::*;
//!
//! # fn get_initial_auth_data(_: &Mechname) -> Vec<u8> { unimplemented!() }
//! # fn tell_other_side_which(_: &Mechname) {}
//! # fn get_more_auth_data() -> Option<Vec<u8>> { unimplemented!() }
//! // the `config` is provided by the user of this crate. The `writer` is a stand-in for sending
//! // data to other side of the authentication exchange.
//! fn sasl_authenticate(config: Arc<SASLConfig>, writer: &mut impl io::Write) {
//!     let sasl = SASLClient::new(config);
//!     // These would normally be provided via the protocol in question
//!     let offered_mechs = &[Mechname::parse(b"PLAIN").unwrap(), Mechname::parse(b"GSSAPI").unwrap()];
//!
//!     // select the best offered mechanism that the user enabled in the `config`
//!     let mut session = sasl.start_suggested(offered_mechs).expect("no shared mechanisms");
//!
//!     // Access to the name of the selected mechanism
//!     let selected_mechanism = session.get_mechname();
//!
//!     let mut data: Option<Vec<u8>> = None;
//!     // Which side needs to send the first bit of data depends on the mechanism
//!     if !session.are_we_first() {
//!         // Tell the other side which mechanism we selected and receive initial auth data
//!         data = Some(get_initial_auth_data(selected_mechanism));
//!     } else {
//!         // If we *are* going first we still need to inform the other party of the selected
//!         // mechanism. Many protocols allow sending the selected mechanism with the initial
//!         // batch of data, but we're not doing that here for simplicities sake.
//!         tell_other_side_which(selected_mechanism);
//!     }
//!
//!     // stepping the authentication exchange to completion
//!     while {
//!         // each call to step writes the generated auth data into the provided writer.
//!         // Normally this data would then have to be sent to the other party, but this goes
//!         // beyond the scope of this example
//!         let state = session.step(data.as_deref(), writer).expect("step errored!");
//!         // returns `true` if step needs to be called again with another batch of data
//!         state.is_running()
//!     } {
//!         // While we aren't finished, receive more data from the other party
//!         data = get_more_auth_data()
//!     }
//!     // Wohoo, we're done!
//!     // rsasl can in most cases not tell if the authentication was successful, this would have
//!     // to be checked in a protocol-specific way.
//! }
//! ```
//!
//! After an authentication exchange has finished [`Session::validation`](session::Session::validation)
//! may be used in server contexts to extract a `Validation` type from the authentication exchange.
//! Further details about `Validation` can be found in the [`validate`] module documentation.
//!
//! To minimize dependencies protocol implementations should always depend on rsasl with the
//! least amount of features enabled:
//! ```toml
//! [dependencies]
//! rsasl = { version = "2", default-features = false, features = ["provider"]}
//! ```
//! or, if they need base64 support:
//! ```Cargo.toml
//! [dependencies]
//! rsasl = { version = "2", default-features = false, features = ["provider_base64"]}
//! ```
//!
//! This makes use of [feature unification](https://doc.rust-lang.org/cargo/reference/features.html#feature-unification)
//! to make rsasl a (near-)zero dependency crate. All decisions about compiled-in mechanism support
//! and other features are put into the hand of the final user.
//!
//! Specifically when depended on this way rsasl does not compile code for *any mechanism* or
//! most of the selection internals, minimizing the compile-time and code size impact.
//! Re-enabling required mechanisms and selection system is deferred to the user of
//! the protocol implementation who will have their own dependency on rsasl if they want to make
//! use of SASL authentication.
//!
//! To this end a protocol crate **should not** re-export anything from the rsasl crate! Doing so
//! may lead to a situation where users can't use any mechanisms since they only depend on
//! rsasl via a transient dependency that has no mechanism features enabled.
//!
// TODO:
//     Bonus minus points: sasl.wrap(data) and sasl.unwrap(data) for security layers. Prefer to
//     not and instead do TLS. Needs better explanation I fear.
//!
//! # Application Code
//!
//! Applications wanting to perform authentication start by constructing a
//! [`SASLConfig`](prelude::SASLConfig).
//!
//! This config selects the mechanisms that will be available, the priority of mechanisms, and
//! sets up provisions for any required authentication data (such the username, password, etc).
//!
//! Authentication data is queried using [`Property`](property::Property)s and
//! [`SessionCallback`](callback::SessionCallback). The [`property`] module documentation
//! contains additional details on the use of `Property`.
//!
//! Mechanisms will generally request a different set of properties.
//! The documentation for each mechanism will list the required properties and any additional
//! limitations that may be put on their values. Currently no discovery of queryable properties is
//! done, so it is the responsibility of the application code resp. the `SASLConfig` to limit
//! mechanisms to only those where all properties can be provided.
//!
//!
//! Additionally, on the server side, `SessionCallback`s can implement the
//! [`SessionCallback::validate`](callback::SessionCallback::validate) method to return data
//! from the authentication exchange to the protocol implementation, e.g. to return the user that
//! was just authenticated. Details for this use-case are found in the [`validate`] module
//! documentation.
//!
//! In addition to selecting mechanisms at runtime, available mechanisms can also be limited at
//! compile time using feature flags. The default feature flags enable all IANA-registered
//! mechanisms in `COMMON` use that are implemented by rsasl. See the module documentation for
//! [`mechanisms`] for further details on how to en-/disable mechanisms.
//!
//!
//! # Custom Mechanisms
//!
//! *NOTE:* **The system to add custom mechanisms is in flux and does not observe the same stability
//! guarantees as the rest of the crate. Breaking changes in this system may happen even for
//! minor changes of the crate version.**
//!
//! Custom mechanism implementations are possible but not stable as of rsasl `2.0.0`. To
//! implement a custom mechanism implementation the feature `unstable_custom_mechanism` must be
//! enabled.
//!
//! A custom mechanism must implement the trait [`Authentication`](mechanism::Authentication) and
//! define a [`Mechanism`](registry::Mechanism) struct describing the implemented mechanism.
//! Documentation about how to add a custom mechanism is found in the [`registry module documentation`](registry).

#![warn(
    clippy::all,
    clippy::pedantic,
    clippy::nursery,
    clippy::exhaustive_enums,
    clippy::exhaustive_structs
)]
#![allow(
    non_upper_case_globals,
    non_camel_case_types,
    clippy::doc_markdown,
    clippy::module_name_repetitions,
    clippy::inline_always,
    clippy::missing_errors_doc,
    clippy::box_default
)]
// FIXME: problems with `registry_static::MECHANISMS` and `linkme::distributed_slice`
#![allow(clippy::exhaustive_enums)]
// Mark rsasl `no_std` if the `std` feature flag is not enabled.
#![cfg_attr(not(any(feature = "std", test)), no_std)]
#[cfg(not(any(feature = "std", test)))]
compile_error!("rsasl can't be compiled without the std feature at the moment, sorry");
extern crate core;
#[cfg(any(feature = "std", test))]
extern crate std as alloc;

// none of these should be necessary for a provider to compile
#[cfg(feature = "config_builder")]
mod builder;
pub mod callback;
pub mod mechanisms;

// Only relevant to a provider
#[cfg(any(feature = "provider", feature = "testutils", test))]
mod sasl;

pub mod config;
mod session;

pub mod property;
mod typed;
pub mod validate;

mod error;
pub mod mechname;

#[cfg(not(any(doc, feature = "unstable_custom_mechanism")))]
mod mechanism;
#[cfg(not(any(doc, feature = "unstable_custom_mechanism")))]
mod registry;

#[cfg(any(doc, feature = "unstable_custom_mechanism"))]
pub mod mechanism;
#[cfg(any(doc, feature = "unstable_custom_mechanism"))]
pub mod registry;

mod channel_bindings;
mod context;

mod vectored_io;

pub mod prelude {
    //! prelude exporting the most commonly used types
    pub use crate::error::{SASLError, SessionError};

    pub use crate::config::SASLConfig;
    pub use crate::mechname::Mechname;
    pub use crate::property::Property;
    pub use crate::registry::{Mechanism, Registry};
    pub use crate::session::{MessageSent, State};
    pub use crate::validate::Validation;

    #[cfg(feature = "provider")]
    pub use crate::channel_bindings::ChannelBindingCallback;
    #[cfg(feature = "provider")]
    pub use crate::sasl::{SASLClient, SASLServer};
    #[cfg(feature = "provider")]
    pub use crate::session::Session;
}

#[cfg(any(test, feature = "testutils"))]
pub mod test;

#[cfg(all(doc, not(doctest)))]
pub mod docs {
    //! Modules purely for documentation

    pub mod readme {
        //! Render of the repositories' README.md:
        #![doc = include_str!("../README.md")]
    }

    pub mod adr {
        //! Architecture design record explaining design decisions

        pub mod adr0001_property_and_validation_newtype {
            #![doc = include_str!("../docs/decisions/0001-property-and-validation-newtype.md")]
        }
        pub mod adr0002_context_vs_provide_any {
            #![doc = include_str!("../docs/decisions/0002-context-vs-provide_any.md")]
        }
        pub mod adr0003_prevent_recursive_callbacks {
            #![doc = include_str!("../docs/decisions/0003-prevent-recursive-callbacks.md")]
        }
    }

    pub mod changelog {
        //! Render of the CHANGELOG file for rsasl
        #![doc = include_str!("../CHANGELOG.md")]
    }

    #[cfg(feature = "document-features")]
    pub mod features {
        //! primer on the use of cargo features in rsasl
        //!
        #![doc = document_features::document_features!()]
    }
}