mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-10-09 12:05:49 +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>> {
|
pub fn sasl_decode_challenge_oauth(challenge: &[u8]) -> Option<Credentials<String>> {
|
||||||
extract_oauth_bearer(challenge).map(|s| Credentials::OAuthBearer { token: s.into() })
|
extract_oauth_bearer(challenge).map(|s| Credentials::OAuthBearer { token: s.into() })
|
||||||
}
|
}
|
||||||
|
@ -86,6 +56,7 @@ fn extract_oauth_bearer(bytes: &[u8]) -> Option<&str> {
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -667,8 +667,11 @@ impl Default for SessionConfig {
|
||||||
mechanisms: IfBlock::new::<Mechanism>(
|
mechanisms: IfBlock::new::<Mechanism>(
|
||||||
"session.auth.mechanisms",
|
"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",
|
"false",
|
||||||
),
|
),
|
||||||
|
|
|
@ -176,8 +176,9 @@ impl Capability {
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
capabilities.extend([
|
capabilities.extend([
|
||||||
Capability::Auth(Mechanism::OAuthBearer),
|
|
||||||
Capability::Auth(Mechanism::Plain),
|
Capability::Auth(Mechanism::Plain),
|
||||||
|
Capability::Auth(Mechanism::OAuthBearer),
|
||||||
|
Capability::Auth(Mechanism::XOauth2),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if offer_tls {
|
if offer_tls {
|
||||||
|
|
|
@ -29,7 +29,7 @@ impl<T: SessionStream> Session<T> {
|
||||||
let mut args = request.parse_authenticate()?;
|
let mut args = request.parse_authenticate()?;
|
||||||
|
|
||||||
match args.mechanism {
|
match args.mechanism {
|
||||||
Mechanism::Plain | Mechanism::OAuthBearer => {
|
Mechanism::Plain | Mechanism::OAuthBearer | Mechanism::XOauth2 => {
|
||||||
if !args.params.is_empty() {
|
if !args.params.is_empty() {
|
||||||
let challenge = base64_decode(args.params.pop().unwrap().as_bytes())
|
let challenge = base64_decode(args.params.pop().unwrap().as_bytes())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl<T: SessionStream> Session<T> {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let credentials = match mechanism {
|
let credentials = match mechanism {
|
||||||
Mechanism::Plain | Mechanism::OAuthBearer => {
|
Mechanism::Plain | Mechanism::OAuthBearer | Mechanism::XOauth2 => {
|
||||||
if !params.is_empty() {
|
if !params.is_empty() {
|
||||||
base64_decode(params.pop().unwrap().as_bytes())
|
base64_decode(params.pop().unwrap().as_bytes())
|
||||||
.and_then(|challenge| {
|
.and_then(|challenge| {
|
||||||
|
|
|
@ -22,9 +22,9 @@ impl<T: SessionStream> Session<T> {
|
||||||
response.extend_from_slice(b"\"STARTTLS\"\r\n");
|
response.extend_from_slice(b"\"STARTTLS\"\r\n");
|
||||||
}
|
}
|
||||||
if self.stream.is_tls() || self.server.core.imap.allow_plain_auth {
|
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 {
|
} else {
|
||||||
response.extend_from_slice(b"\"SASL\" \"OAUTHBEARER\"\r\n");
|
response.extend_from_slice(b"\"SASL\" \"OAUTHBEARER XOAUTH2\"\r\n");
|
||||||
};
|
};
|
||||||
if let Some(sieve) =
|
if let Some(sieve) =
|
||||||
self.server
|
self.server
|
||||||
|
|
|
@ -27,7 +27,7 @@ impl<T: SessionStream> Session<T> {
|
||||||
mut params: Vec<String>,
|
mut params: Vec<String>,
|
||||||
) -> trc::Result<()> {
|
) -> trc::Result<()> {
|
||||||
match mechanism {
|
match mechanism {
|
||||||
Mechanism::Plain | Mechanism::OAuthBearer => {
|
Mechanism::Plain | Mechanism::OAuthBearer | Mechanism::XOauth2 => {
|
||||||
if !params.is_empty() {
|
if !params.is_empty() {
|
||||||
let credentials = base64_decode(params.pop().unwrap().as_bytes())
|
let credentials = base64_decode(params.pop().unwrap().as_bytes())
|
||||||
.and_then(|challenge| {
|
.and_then(|challenge| {
|
||||||
|
|
|
@ -19,9 +19,9 @@ pub mod list;
|
||||||
impl<T: SessionStream> Session<T> {
|
impl<T: SessionStream> Session<T> {
|
||||||
pub async fn handle_capa(&mut self) -> trc::Result<()> {
|
pub async fn handle_capa(&mut self) -> trc::Result<()> {
|
||||||
let mechanisms = if self.stream.is_tls() || self.server.core.imap.allow_plain_auth {
|
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 {
|
} else {
|
||||||
vec![Mechanism::OAuthBearer]
|
vec![Mechanism::OAuthBearer, Mechanism::XOauth2]
|
||||||
};
|
};
|
||||||
|
|
||||||
trc::event!(
|
trc::event!(
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
use common::{
|
use common::{
|
||||||
auth::{
|
auth::{
|
||||||
AuthRequest,
|
AuthRequest,
|
||||||
sasl::{
|
sasl::{sasl_decode_challenge_oauth, sasl_decode_challenge_plain},
|
||||||
sasl_decode_challenge_oauth, sasl_decode_challenge_plain, sasl_decode_challenge_xoauth,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
listener::SessionStream,
|
listener::SessionStream,
|
||||||
};
|
};
|
||||||
|
@ -38,21 +36,13 @@ impl SaslToken {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
AUTH_OAUTHBEARER => SaslToken {
|
AUTH_OAUTHBEARER | AUTH_XOAUTH2 => SaslToken {
|
||||||
mechanism,
|
mechanism,
|
||||||
credentials: Credentials::OAuthBearer {
|
credentials: Credentials::OAuthBearer {
|
||||||
token: String::new(),
|
token: String::new(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
AUTH_XOAUTH2 => SaslToken {
|
|
||||||
mechanism,
|
|
||||||
credentials: Credentials::XOauth2 {
|
|
||||||
username: String::new(),
|
|
||||||
secret: String::new(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,17 +86,11 @@ impl<T: SessionStream> Session<T> {
|
||||||
.await
|
.await
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(AUTH_OAUTHBEARER, _) => {
|
(AUTH_OAUTHBEARER | AUTH_XOAUTH2, _) => {
|
||||||
if let Some(credentials) = sasl_decode_challenge_oauth(&response) {
|
if let Some(credentials) = sasl_decode_challenge_oauth(&response) {
|
||||||
return self.authenticate(credentials).await;
|
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