trivy / Check (pull_request) Waiting to run
Upstream commit: 9d1c75ab68435e4417337f768291e5f947686203 Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 118 in 50 files Dangling module declarations removed: 5 Edits turning enterprise off: 25 Third-party code: 14 files, 0 not in THIRD-PARTY.md Verification: clean One snippet more than v0.16.22, in crates/common/src/auth/authentication.rs (3, was 2).
209 lines
7.4 KiB
Rust
209 lines
7.4 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use crate::core::Session;
|
|
use ahash::AHashMap;
|
|
use common::USER_AGENT;
|
|
use mail_auth::report::{
|
|
ActionDisposition, AuthFailureType, DeliveryResult, DmarcResult, Feedback, FeedbackType,
|
|
Report, tlsrpt::TlsReport,
|
|
};
|
|
use std::{collections::hash_map::Entry, time::SystemTime};
|
|
use store::write::now;
|
|
use tokio::io::{AsyncRead, AsyncWrite};
|
|
use trc::IncomingReportEvent;
|
|
|
|
impl<T: AsyncWrite + AsyncRead + Unpin> Session<T> {
|
|
pub fn new_auth_failure(&self, ft: AuthFailureType, rejected: bool) -> Feedback<'_> {
|
|
Feedback::new(FeedbackType::AuthFailure)
|
|
.with_auth_failure(ft)
|
|
.with_arrival_date(
|
|
SystemTime::now()
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.map_or(0, |d| d.as_secs()) as i64,
|
|
)
|
|
.with_source_ip(self.data.remote_ip)
|
|
.with_reporting_mta(&self.hostname)
|
|
.with_user_agent(USER_AGENT)
|
|
.with_delivery_result(if rejected {
|
|
DeliveryResult::Reject
|
|
} else {
|
|
DeliveryResult::Unspecified
|
|
})
|
|
}
|
|
|
|
pub fn is_report(&self) -> bool {
|
|
let analysis = &self.server.core.smtp.report.analysis;
|
|
|
|
self.data
|
|
.rcpt_to
|
|
.iter()
|
|
.any(|addr| analysis.is_report_address(addr.orig_address()))
|
|
}
|
|
}
|
|
|
|
pub(crate) trait LogReport {
|
|
fn log(&self);
|
|
}
|
|
|
|
impl LogReport for Report {
|
|
fn log(&self) {
|
|
let mut dmarc_pass = 0;
|
|
let mut dmarc_quarantine = 0;
|
|
let mut dmarc_reject = 0;
|
|
let mut dmarc_none = 0;
|
|
let mut dkim_pass = 0;
|
|
let mut dkim_fail = 0;
|
|
let mut dkim_none = 0;
|
|
let mut spf_pass = 0;
|
|
let mut spf_fail = 0;
|
|
let mut spf_none = 0;
|
|
|
|
for record in self.records() {
|
|
let count = std::cmp::min(record.count(), 1);
|
|
|
|
match record.action_disposition() {
|
|
ActionDisposition::Pass => {
|
|
dmarc_pass += count;
|
|
}
|
|
ActionDisposition::Quarantine => {
|
|
dmarc_quarantine += count;
|
|
}
|
|
ActionDisposition::Reject => {
|
|
dmarc_reject += count;
|
|
}
|
|
ActionDisposition::None | ActionDisposition::Unspecified => {
|
|
dmarc_none += count;
|
|
}
|
|
}
|
|
match record.dmarc_dkim_result() {
|
|
DmarcResult::Pass => {
|
|
dkim_pass += count;
|
|
}
|
|
DmarcResult::Fail => {
|
|
dkim_fail += count;
|
|
}
|
|
DmarcResult::Unspecified => {
|
|
dkim_none += count;
|
|
}
|
|
}
|
|
match record.dmarc_spf_result() {
|
|
DmarcResult::Pass => {
|
|
spf_pass += count;
|
|
}
|
|
DmarcResult::Fail => {
|
|
spf_fail += count;
|
|
}
|
|
DmarcResult::Unspecified => {
|
|
spf_none += count;
|
|
}
|
|
}
|
|
}
|
|
|
|
trc::event!(
|
|
IncomingReport(
|
|
if (dmarc_reject + dmarc_quarantine + dkim_fail + spf_fail) > 0 {
|
|
IncomingReportEvent::DmarcReportWithWarnings
|
|
} else {
|
|
IncomingReportEvent::DmarcReport
|
|
}
|
|
),
|
|
RangeFrom = trc::Value::Timestamp(self.date_range_begin()),
|
|
RangeTo = trc::Value::Timestamp(self.date_range_end()),
|
|
Domain = self.domain().to_string(),
|
|
From = self.email().to_string(),
|
|
Id = self.report_id().to_string(),
|
|
DmarcPass = dmarc_pass,
|
|
DmarcQuarantine = dmarc_quarantine,
|
|
DmarcReject = dmarc_reject,
|
|
DmarcNone = dmarc_none,
|
|
DkimPass = dkim_pass,
|
|
DkimFail = dkim_fail,
|
|
DkimNone = dkim_none,
|
|
SpfPass = spf_pass,
|
|
SpfFail = spf_fail,
|
|
SpfNone = spf_none,
|
|
);
|
|
}
|
|
}
|
|
|
|
impl LogReport for TlsReport {
|
|
fn log(&self) {
|
|
for policy in self.policies.iter().take(5) {
|
|
let mut details = AHashMap::with_capacity(policy.failure_details.len());
|
|
for failure in &policy.failure_details {
|
|
let num_failures = std::cmp::min(1, failure.failed_session_count);
|
|
match details.entry(failure.result_type) {
|
|
Entry::Occupied(mut e) => {
|
|
*e.get_mut() += num_failures;
|
|
}
|
|
Entry::Vacant(e) => {
|
|
e.insert(num_failures);
|
|
}
|
|
}
|
|
}
|
|
|
|
trc::event!(
|
|
IncomingReport(if policy.summary.total_failure > 0 {
|
|
IncomingReportEvent::TlsReportWithWarnings
|
|
} else {
|
|
IncomingReportEvent::TlsReport
|
|
}),
|
|
RangeFrom =
|
|
trc::Value::Timestamp(self.date_range.start_datetime.to_timestamp() as u64),
|
|
RangeTo = trc::Value::Timestamp(self.date_range.end_datetime.to_timestamp() as u64),
|
|
Domain = policy.policy.policy_domain.clone(),
|
|
From = self.contact_info.as_deref().unwrap_or_default().to_string(),
|
|
Id = self.report_id.clone(),
|
|
Policy = format!("{:?}", policy.policy.policy_type),
|
|
TotalSuccesses = policy.summary.total_success,
|
|
TotalFailures = policy.summary.total_failure,
|
|
Details = format!("{details:?}"),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LogReport for Feedback<'_> {
|
|
fn log(&self) {
|
|
trc::event!(
|
|
IncomingReport(match self.feedback_type() {
|
|
mail_auth::report::FeedbackType::Abuse => IncomingReportEvent::AbuseReport,
|
|
mail_auth::report::FeedbackType::AuthFailure =>
|
|
IncomingReportEvent::AuthFailureReport,
|
|
mail_auth::report::FeedbackType::Fraud => IncomingReportEvent::FraudReport,
|
|
mail_auth::report::FeedbackType::NotSpam => IncomingReportEvent::NotSpamReport,
|
|
mail_auth::report::FeedbackType::Other => IncomingReportEvent::OtherReport,
|
|
mail_auth::report::FeedbackType::Virus => IncomingReportEvent::VirusReport,
|
|
}),
|
|
RangeFrom = trc::Value::Timestamp(
|
|
self.arrival_date()
|
|
.map(|d| d as u64)
|
|
.unwrap_or_else(|| { now() })
|
|
),
|
|
Domain = self
|
|
.reported_domain()
|
|
.iter()
|
|
.map(|d| trc::Value::String(d.as_ref().into()))
|
|
.collect::<Vec<_>>(),
|
|
Hostname = self.reporting_mta().map(|d| trc::Value::String(d.into())),
|
|
Url = self
|
|
.reported_uri()
|
|
.iter()
|
|
.map(|d| trc::Value::String(d.as_ref().into()))
|
|
.collect::<Vec<_>>(),
|
|
RemoteIp = self.source_ip(),
|
|
Total = self.incidents(),
|
|
Result = format!("{:?}", self.delivery_result()),
|
|
Details = self
|
|
.authentication_results()
|
|
.iter()
|
|
.map(|d| trc::Value::String(d.as_ref().into()))
|
|
.collect::<Vec<_>>(),
|
|
);
|
|
}
|
|
}
|