[Web] Mail policies, groups: check domain access first

This commit is contained in:
FreddleSpl0it committed 2026-11-09 12:19:07 +01:00
1 parent c304040140
commit 03ce9fe879
4 files changed
+33 -11

No files matched your search

@@ -168,13 +168,14 @@ class PolicyService
/** @throws UserError */
private function domainFor(mixed $value): string
{
$domain = strtolower((string)Input::text($value));
$domain = Input::text($value);
$domain = ($domain === null || $domain === '') ? '' : (string)idn_to_ascii(strtolower($domain), 0, INTL_IDNA_VARIANT_UTS46);
if ($domain !== '' && !$this->context->hasAdminRights() && !$this->context->canAccessDomain($domain)) {
throw new AccessDenied();
}
if ($domain === '' || !$this->repo->isDomain($domain)) {
throw new UserError('domain_not_found', [htmlspecialchars((string)Input::text($value))]);
}
if (!$this->context->hasAdminRights() && !$this->context->canAccessDomain($domain)) {
throw new AccessDenied();
}
return $domain;
}
@@ -195,12 +195,12 @@ class GroupService
{
$domain = Input::text($value);
$domain = ($domain === null || $domain === '') ? '' : (string)idn_to_ascii(strtolower($domain), 0, INTL_IDNA_VARIANT_UTS46);
if ($domain !== '' && !$this->context->hasAdminRights() && !$this->context->canAccessDomain($domain)) {
throw new AccessDenied();
}
if ($domain === '' || !$this->lookup->isDomain($domain)) {
throw new UserError('domain_not_found', [htmlspecialchars((string)Input::text($value))]);
}
if (!$this->context->hasAdminRights() && !$this->context->canAccessDomain($domain)) {
throw new AccessDenied();
}
return $domain;
}
@@ -304,6 +304,9 @@ class GroupService
$new = array_values(array_diff(array_unique($add), array_map('strval', array_keys($members))));
$regular = $this->lookup->regularMailboxes($new);
foreach ($new as $username) {
if ($this->foreignDomain($username)) {
throw new AccessDenied();
}
if (!in_array($username, $regular, true)) {
throw new UserError('group_member_invalid', [htmlspecialchars($username)]);
}
@@ -337,12 +340,12 @@ class GroupService
// The group's own address is covered by sender_allowed
$valid = ($address !== null && $address !== $group->address && $this->lookup->aliasExists($address));
}
if ($this->foreignDomain($address)) {
throw new AccessDenied();
}
if (!$valid) {
throw new UserError('group_send_as_invalid', [htmlspecialchars($value)]);
}
if (!$this->context->hasAdminRights() && !$this->context->canAccessDomain(self::domainOf((string)$address))) {
throw new AccessDenied();
}
$sendAs[] = (string)$address;
}
$sendAs = array_values(array_unique($sendAs));
@@ -505,4 +508,11 @@ class GroupService
{
return substr($address, strrpos($address, '@') + 1);
}
/** A domain admin names an address or @domain outside their domains: denied before anything is looked up */
private function foreignDomain(?string $address): bool
{
$domain = ($address !== null && str_contains($address, '@')) ? self::domainOf($address) : '';
return $domain !== '' && !$this->context->hasAdminRights() && !$this->context->canAccessDomain($domain);
}
}