mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-10-09 03:55:45 +08:00
parent
f667da0d4f
commit
dcdf68b774
9 changed files with 18 additions and 59 deletions
|
@ -30,36 +30,6 @@ pub fn sasl_decode_challenge_plain(challenge: &[u8]) -> Option<Credentials<Strin
|
|||
}
|
||||
}
|
||||
|
||||
pub fn sasl_decode_challenge_xoauth(challenge: &[u8]) -> Option<Credentials<String>> {
|
||||
let mut b_username = Vec::new();
|
||||
let mut b_secret = Vec::new();
|
||||
let mut arg_num = 0;
|
||||
let mut in_arg = false;
|
||||
|
||||
for &ch in challenge {
|
||||
if in_arg {
|
||||
if ch != 1 {
|
||||
if arg_num == 1 {
|
||||
b_username.push(ch);
|
||||
} else if arg_num == 2 {
|
||||
b_secret.push(ch);
|
||||
}
|
||||
} else {
|
||||
in_arg = false;
|
||||
}
|
||||
} else if ch == b'=' {
|
||||
arg_num += 1;
|
||||
in_arg = true;
|
||||
}
|
||||
}
|
||||
match (String::from_utf8(b_username), String::from_utf8(b_secret)) {
|
||||
(Ok(s_username), Ok(s_secret)) if !s_username.is_empty() => {
|
||||
Some((s_username, s_secret).into())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sasl_decode_challenge_oauth(challenge: &[u8]) -> Option<Credentials<String>> {
|
||||
extract_oauth_bearer(challenge).map(|s| Credentials::OAuthBearer { token: s.into() })
|
||||
}
|
||||
|
@ -86,6 +56,7 @@ fn extract_oauth_bearer(bytes: &[u8]) -> Option<&str> {
|
|||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -667,8 +667,11 @@ impl Default for SessionConfig {
|
|||
mechanisms: IfBlock::new::<Mechanism>(
|
||||
"session.auth.mechanisms",
|
||||
[
|
||||
("local_port != 25 && is_tls", "[plain, login, oauthbearer]"),
|
||||
("local_port != 25", "[oauthbearer]"),
|
||||
(
|
||||
"local_port != 25 && is_tls",
|
||||
"[plain, login, oauthbearer, xoauth2]",
|
||||
),
|
||||
("local_port != 25", "[oauthbearer, xoauth2]"),
|
||||
],
|
||||
"false",
|
||||
),
|
||||
|
|
|
@ -176,8 +176,9 @@ impl Capability {
|
|||
]);
|
||||
} else {
|
||||
capabilities.extend([
|
||||
Capability::Auth(Mechanism::OAuthBearer),
|
||||
Capability::Auth(Mechanism::Plain),
|
||||
Capability::Auth(Mechanism::OAuthBearer),
|
||||
Capability::Auth(Mechanism::XOauth2),
|
||||
]);
|
||||
}
|
||||
if offer_tls {
|
||||
|
|
|
@ -29,7 +29,7 @@ impl<T: SessionStream> Session<T> {
|
|||
let mut args = request.parse_authenticate()?;
|
||||
|
||||
match args.mechanism {
|
||||
Mechanism::Plain | Mechanism::OAuthBearer => {
|
||||
Mechanism::Plain | Mechanism::OAuthBearer | Mechanism::XOauth2 => {
|
||||
if !args.params.is_empty() {
|
||||
let challenge = base64_decode(args.params.pop().unwrap().as_bytes())
|
||||
.ok_or_else(|| {
|
||||
|
|
|
@ -37,7 +37,7 @@ impl<T: SessionStream> Session<T> {
|
|||
.collect();
|
||||
|
||||
let credentials = match mechanism {
|
||||
Mechanism::Plain | Mechanism::OAuthBearer => {
|
||||
Mechanism::Plain | Mechanism::OAuthBearer | Mechanism::XOauth2 => {
|
||||
if !params.is_empty() {
|
||||
base64_decode(params.pop().unwrap().as_bytes())
|
||||
.and_then(|challenge| {
|
||||
|
|
|
@ -22,9 +22,9 @@ impl<T: SessionStream> Session<T> {
|
|||
response.extend_from_slice(b"\"STARTTLS\"\r\n");
|
||||
}
|
||||
if self.stream.is_tls() || self.server.core.imap.allow_plain_auth {
|
||||
response.extend_from_slice(b"\"SASL\" \"PLAIN OAUTHBEARER\"\r\n");
|
||||
response.extend_from_slice(b"\"SASL\" \"PLAIN OAUTHBEARER XOAUTH2\"\r\n");
|
||||
} else {
|
||||
response.extend_from_slice(b"\"SASL\" \"OAUTHBEARER\"\r\n");
|
||||
response.extend_from_slice(b"\"SASL\" \"OAUTHBEARER XOAUTH2\"\r\n");
|
||||
};
|
||||
if let Some(sieve) =
|
||||
self.server
|
||||
|
|
|
@ -27,7 +27,7 @@ impl<T: SessionStream> Session<T> {
|
|||
mut params: Vec<String>,
|
||||
) -> trc::Result<()> {
|
||||
match mechanism {
|
||||
Mechanism::Plain | Mechanism::OAuthBearer => {
|
||||
Mechanism::Plain | Mechanism::OAuthBearer | Mechanism::XOauth2 => {
|
||||
if !params.is_empty() {
|
||||
let credentials = base64_decode(params.pop().unwrap().as_bytes())
|
||||
.and_then(|challenge| {
|
||||
|
|
|
@ -19,9 +19,9 @@ pub mod list;
|
|||
impl<T: SessionStream> Session<T> {
|
||||
pub async fn handle_capa(&mut self) -> trc::Result<()> {
|
||||
let mechanisms = if self.stream.is_tls() || self.server.core.imap.allow_plain_auth {
|
||||
vec![Mechanism::Plain, Mechanism::OAuthBearer]
|
||||
vec![Mechanism::Plain, Mechanism::OAuthBearer, Mechanism::XOauth2]
|
||||
} else {
|
||||
vec![Mechanism::OAuthBearer]
|
||||
vec![Mechanism::OAuthBearer, Mechanism::XOauth2]
|
||||
};
|
||||
|
||||
trc::event!(
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
use common::{
|
||||
auth::{
|
||||
AuthRequest,
|
||||
sasl::{
|
||||
sasl_decode_challenge_oauth, sasl_decode_challenge_plain, sasl_decode_challenge_xoauth,
|
||||
},
|
||||
sasl::{sasl_decode_challenge_oauth, sasl_decode_challenge_plain},
|
||||
},
|
||||
listener::SessionStream,
|
||||
};
|
||||
|
@ -38,21 +36,13 @@ impl SaslToken {
|
|||
},
|
||||
}
|
||||
.into(),
|
||||
AUTH_OAUTHBEARER => SaslToken {
|
||||
AUTH_OAUTHBEARER | AUTH_XOAUTH2 => SaslToken {
|
||||
mechanism,
|
||||
credentials: Credentials::OAuthBearer {
|
||||
token: String::new(),
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
AUTH_XOAUTH2 => SaslToken {
|
||||
mechanism,
|
||||
credentials: Credentials::XOauth2 {
|
||||
username: String::new(),
|
||||
secret: String::new(),
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -96,17 +86,11 @@ impl<T: SessionStream> Session<T> {
|
|||
.await
|
||||
};
|
||||
}
|
||||
(AUTH_OAUTHBEARER, _) => {
|
||||
(AUTH_OAUTHBEARER | AUTH_XOAUTH2, _) => {
|
||||
if let Some(credentials) = sasl_decode_challenge_oauth(&response) {
|
||||
return self.authenticate(credentials).await;
|
||||
}
|
||||
}
|
||||
(AUTH_XOAUTH2, _) => {
|
||||
if let Some(credentials) = sasl_decode_challenge_xoauth(&response) {
|
||||
return self.authenticate(credentials).await;
|
||||
}
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue