Compare commits

...
6 changed files with 30 additions and 6 deletions

No files matched your search

+3 -2
View File
@@ -199,7 +199,8 @@ plugin {
sieve_plugins = sieve_imapsieve sieve_extprograms
sieve_vacation_send_from_recipient = yes
sieve_redirect_envelope_from = recipient
# Notifications are sent with the address of the mailbox (not :from or postmaster), so mail policies apply
# Notifications are sent with the address of the mailbox (not :from or postmaster), so mail policies apply;
# the old notify extension always sends as postmaster and is on by default, so sieve_extensions disables it
sieve_notify_mailto_envelope_from = recipient
# From elsewhere to Spam folder
imapsieve_mailbox1_name = Junk
@@ -217,7 +218,7 @@ plugin {
quota_warning2 = storage=80%% quota-warning 80 %u
sieve_pipe_bin_dir = /usr/lib/dovecot/sieve
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
sieve_extensions = +notify +imapflags +vacation-seconds +editheader
sieve_extensions = -notify +imapflags +vacation-seconds +editheader
sieve_max_script_size = 1M
sieve_max_redirects = 100
sieve_max_actions = 101
+2 -1
View File
@@ -121,9 +121,10 @@ smtp_sender_cleanup unix n - y - 0 cleanup
mailcow-policy unix - n n - 0 spawn user=nobody argv=/usr/local/bin/mailcow-policy.py
# start watchdog-specific
# logs to local7 (hidden)
# logs to local7 (hidden), no mail policy check
589 inet n - n - - smtpd
-o smtpd_client_restrictions=permit_mynetworks,reject
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o syslog_name=watchdog
-o syslog_facility=local7
-o smtpd_milters=
@@ -105,9 +105,11 @@ class PolicyEngine
}
}
return match ($mode) {
'all' => true,
'internal' => $internal || $this->any($allow, $matches),
'list' => $this->any($allow, $matches),
default => true,
// an unknown mode lets nothing through
default => false,
};
}
@@ -288,11 +288,15 @@ class PolicyService
}
$entry = 'group:' . $id;
} elseif (str_contains(ltrim($entry, '@'), '@')) {
if (!filter_var($entry, FILTER_VALIDATE_EMAIL)) {
// in the form the engine compares: domain without the final dot, never a +tag or an IP literal
$at = strrpos($entry, '@');
$local = substr($entry, 0, $at);
$entry = $local . '@' . rtrim(substr($entry, $at + 1), '.');
if (!filter_var($entry, FILTER_VALIDATE_EMAIL) || str_contains(substr($local, 1), '+') || str_contains($entry, '@[')) {
throw new UserError('mail_policy_entry_invalid', [htmlspecialchars($entry)]);
}
} else {
$entry = ltrim($entry, '@');
$entry = rtrim(ltrim($entry, '@'), '.');
if (!str_contains($entry, '.') || !filter_var($entry, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
throw new UserError('mail_policy_entry_invalid', [htmlspecialchars($entry)]);
}
@@ -58,6 +58,13 @@ class PolicyEngineTest extends TestCase
$this->assertSame('DUNNO', $this->engine->check('azubi@firma.de', 'x@extern.example', 'azubi@firma.de')->action());
}
public function testUnknownModeLetsNothingThrough(): void
{
$this->lookup->mailboxPolicies['azubi@firma.de'] = [self::policy(1, ['sendMode' => 'nobody', 'receiveMode' => ''])];
$this->assertSame([false, 'send'], $this->check('azubi@firma.de', 'kollege@firma.de', 'azubi@firma.de'));
$this->assertSame([false, 'receive'], $this->check('kollege@firma.de', 'azubi@firma.de', 'kollege@firma.de'));
}
public function testSendOnlyInternalWithAllowedAndBlockedEntries(): void
{
$this->lookup->mailboxPolicies['azubi@firma.de'] = [self::policy(1, [
@@ -113,6 +113,9 @@ class PolicyServiceTest extends TestCase
'mode' => [['send_mode' => 'nobody'], 'mail_policy_mode_invalid'],
'domain without dot' => [['send_allow' => 'localhost'], ['mail_policy_entry_invalid', 'localhost']],
'address' => [['receive_block' => 'a@@b.de'], ['mail_policy_entry_invalid', 'a@@b.de']],
'address with +tag' => [['receive_block' => 'news+promo@vendor.example'], ['mail_policy_entry_invalid', 'news+promo@vendor.example']],
'IP literal' => [['send_allow' => 'u@[127.0.0.1]'], ['mail_policy_entry_invalid', 'u@[127.0.0.1]']],
'top level domain' => [['send_block' => 'com.'], ['mail_policy_entry_invalid', 'com']],
'unknown group' => [['receive_block_groups' => ['8']], ['mail_policy_entry_invalid', 'group:8']],
'reject text with line break' => [['reject_text' => "a\r\nb"], 'mail_policy_reject_text_invalid'],
'reject text not ASCII' => [['reject_text' => 'Nur für Azubis'], 'mail_policy_reject_text_invalid'],
@@ -122,6 +125,12 @@ class PolicyServiceTest extends TestCase
];
}
public function testEntriesInTheFormTheEngineCompares(): void
{
$policy = $this->service()->create(['domain' => 'firma.de', 'name' => 'Test', 'receive_block' => "Spam.Example.\n@news.example\nBoss@Ext.Example.\n+x@ext.example"]);
$this->assertSame(['spam.example', 'news.example', 'boss@ext.example', '+x@ext.example'], $policy->receiveBlock);
}
#[DataProvider('invalidInput')]
public function testInvalidInput(array $input, string|array $error): void
{