diff --git a/crates/smtp/src/inbound/mail.rs b/crates/smtp/src/inbound/mail.rs index 8aa74861..6f40c4d7 100644 --- a/crates/smtp/src/inbound/mail.rs +++ b/crates/smtp/src/inbound/mail.rs @@ -542,6 +542,25 @@ impl Session { ) .await, ) { + // Do not send SPF auth failures to local domains, as they are likely relay attempts (which are blocked later on) + match self + .server + .core + .storage + .directory + .is_local_domain(recipient.domain_part()) + .await + { + Ok(true) => return Ok(result), + Ok(false) => (), + Err(err) => { + trc::error!(err + .caused_by(trc::location!()) + .span_id(self.data.session_id) + .details("Failed to lookup local domain")); + } + } + self.send_spf_report(recipient, &rate, !result, spf_output) .await; } diff --git a/crates/smtp/src/inbound/rcpt.rs b/crates/smtp/src/inbound/rcpt.rs index aab0be1e..6da5b48b 100644 --- a/crates/smtp/src/inbound/rcpt.rs +++ b/crates/smtp/src/inbound/rcpt.rs @@ -304,33 +304,36 @@ impl Session { async fn rcpt_error(&mut self, response: &[u8]) -> Result<(), ()> { tokio::time::sleep(self.params.rcpt_errors_wait).await; self.data.rcpt_errors += 1; - self.write(response).await?; - if self.data.rcpt_errors < self.params.rcpt_errors_max { - Ok(()) - } else { - match self.server.is_rcpt_fail2banned(self.data.remote_ip).await { - Ok(true) => { - trc::event!( - Security(SecurityEvent::BruteForceBan), - SpanId = self.data.session_id, - RemoteIp = self.data.remote_ip, - ); - } - Ok(false) => { + let has_too_many_errors = self.data.rcpt_errors >= self.params.rcpt_errors_max; + + match self.server.is_rcpt_fail2banned(self.data.remote_ip).await { + Ok(true) => { + trc::event!( + Security(SecurityEvent::BruteForceBan), + SpanId = self.data.session_id, + RemoteIp = self.data.remote_ip, + ); + } + Ok(false) => { + if has_too_many_errors { trc::event!( Smtp(SmtpEvent::TooManyInvalidRcpt), SpanId = self.data.session_id, Limit = self.params.rcpt_errors_max, ); } - Err(err) => { - trc::error!(err - .span_id(self.data.session_id) - .caused_by(trc::location!()) - .details("Failed to check if IP should be banned.")); - } } + Err(err) => { + trc::error!(err + .span_id(self.data.session_id) + .caused_by(trc::location!()) + .details("Failed to check if IP should be banned.")); + } + } + if !has_too_many_errors { + self.write(response).await + } else { self.write(b"421 4.3.0 Too many errors, disconnecting.\r\n") .await?; Err(()) diff --git a/tests/src/jmap/auth_oauth.rs b/tests/src/jmap/auth_oauth.rs index 85558391..7230759f 100644 --- a/tests/src/jmap/auth_oauth.rs +++ b/tests/src/jmap/auth_oauth.rs @@ -103,6 +103,7 @@ pub async fn test(params: &mut JMAPTest) { &OAuthCodeRequest::Code { client_id: client_id.to_string(), redirect_uri: "https://localhost".to_string().into(), + nonce: "abc1234".to_string().into(), }, ) .await @@ -136,7 +137,6 @@ pub async fn test(params: &mut JMAPTest) { // Obtain token token_params.insert("redirect_uri".to_string(), "https://localhost".to_string()); - token_params.insert("nonce".to_string(), "abc1234".to_string()); let (token, refresh_token, id_token) = unwrap_oidc_token_response(post(&metadata.token_endpoint, &token_params).await);