mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 18:22:41 +01:00
Prevent inbound processing of the "void/placeholder" address and other reserved addresses
Summary: Depends on D19952. Ref T13222. Never process mail targets if they match: - The "default" address which we send mail "From". - The "void" address which we use as a placholder "To" when we only have "CC" addresses. - Any address from a list of reserved/administrative names. The first two prevent loops. The third one prevents abuse. There's a reasonably well-annotated list of reservations and reasons here: https://webmasters.stackexchange.com/questions/104811/is-there-any-list-of-email-addresses-reserved-because-of-security-concerns-for-a Stuff like `support@` seems fine; stuff like `ssladmin@` might let you get SSL certs issued for a domain you don't control. Also, forbid users from creating application emails with these reserved addresses. Finally, build the default and void addresses somewhat more cleverly. Test Plan: Added unit tests, tried to configured reserved addresses, hit the default/void cases manually with `bin/mail receive-test`. Reviewers: amckinley Reviewed By: amckinley Subscribers: olexiy.myronenko Maniphest Tasks: T13222 Differential Revision: https://secure.phabricator.com/D19953
This commit is contained in:
parent
e3aa043a02
commit
a37b28ef79
6 changed files with 112 additions and 9 deletions
|
@ -191,10 +191,7 @@ EODOC
|
||||||
$this->newOption('cluster.mailers', 'cluster.mailers', array())
|
$this->newOption('cluster.mailers', 'cluster.mailers', array())
|
||||||
->setHidden(true)
|
->setHidden(true)
|
||||||
->setDescription($mailers_description),
|
->setDescription($mailers_description),
|
||||||
$this->newOption(
|
$this->newOption('metamta.default-address', 'string', null)
|
||||||
'metamta.default-address',
|
|
||||||
'string',
|
|
||||||
'noreply@phabricator.example.com')
|
|
||||||
->setDescription(pht('Default "From" address.')),
|
->setDescription(pht('Default "From" address.')),
|
||||||
$this->newOption(
|
$this->newOption(
|
||||||
'metamta.one-mail-per-recipient',
|
'metamta.one-mail-per-recipient',
|
||||||
|
|
|
@ -104,6 +104,16 @@ final class PhabricatorMetaMTAApplicationEmailEditor
|
||||||
pht('Invalid'),
|
pht('Invalid'),
|
||||||
pht('Email address is not formatted properly.'));
|
pht('Email address is not formatted properly.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$address = new PhutilEmailAddress($email);
|
||||||
|
if (PhabricatorMailUtil::isReservedAddress($address)) {
|
||||||
|
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||||
|
$type,
|
||||||
|
pht('Reserved'),
|
||||||
|
pht(
|
||||||
|
'This email address is reserved. Choose a different '.
|
||||||
|
'address.'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$missing = $this->validateIsEmptyTextField(
|
$missing = $this->validateIsEmptyTextField(
|
||||||
|
|
|
@ -41,4 +41,30 @@ final class PhabricatorMailReceiverTestCase extends PhabricatorTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testReservedAddresses() {
|
||||||
|
$default_address = id(new PhabricatorMetaMTAMail())
|
||||||
|
->newDefaultEmailAddress();
|
||||||
|
|
||||||
|
$void_address = id(new PhabricatorMetaMTAMail())
|
||||||
|
->newVoidEmailAddress();
|
||||||
|
|
||||||
|
$map = array(
|
||||||
|
'alincoln@example.com' => false,
|
||||||
|
'sysadmin@example.com' => true,
|
||||||
|
'hostmaster@example.com' => true,
|
||||||
|
'"Walter Ebmaster" <webmaster@example.com>' => true,
|
||||||
|
(string)$default_address => true,
|
||||||
|
(string)$void_address => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($map as $raw_address => $expect) {
|
||||||
|
$address = new PhutilEmailAddress($raw_address);
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
$expect,
|
||||||
|
PhabricatorMailUtil::isReservedAddress($address),
|
||||||
|
pht('Reserved: %s', $raw_address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -713,7 +713,7 @@ final class PhabricatorMetaMTAMail
|
||||||
$actors = $this->loadAllActors();
|
$actors = $this->loadAllActors();
|
||||||
$deliverable_actors = $this->filterDeliverableActors($actors);
|
$deliverable_actors = $this->filterDeliverableActors($actors);
|
||||||
|
|
||||||
$default_from = PhabricatorEnv::getEnvConfig('metamta.default-address');
|
$default_from = (string)$this->newDefaultEmailAddress();
|
||||||
if (empty($params['from'])) {
|
if (empty($params['from'])) {
|
||||||
$mailer->setFrom($default_from);
|
$mailer->setFrom($default_from);
|
||||||
}
|
}
|
||||||
|
@ -1463,18 +1463,33 @@ final class PhabricatorMetaMTAMail
|
||||||
}
|
}
|
||||||
|
|
||||||
private function newMailDomain() {
|
private function newMailDomain() {
|
||||||
|
$domain = PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain');
|
||||||
|
if (strlen($domain)) {
|
||||||
|
return $domain;
|
||||||
|
}
|
||||||
|
|
||||||
$install_uri = PhabricatorEnv::getURI('/');
|
$install_uri = PhabricatorEnv::getURI('/');
|
||||||
$install_uri = new PhutilURI($install_uri);
|
$install_uri = new PhutilURI($install_uri);
|
||||||
|
|
||||||
return $install_uri->getDomain();
|
return $install_uri->getDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newVoidEmailAddress() {
|
public function newDefaultEmailAddress() {
|
||||||
|
$raw_address = PhabricatorEnv::getEnvConfig('metamta.default-address');
|
||||||
|
if (strlen($raw_address)) {
|
||||||
|
return new PhutilEmailAddress($raw_address);
|
||||||
|
}
|
||||||
|
|
||||||
$domain = $this->newMailDomain();
|
$domain = $this->newMailDomain();
|
||||||
$address = "void-recipient@{$domain}";
|
$address = "noreply@{$domain}";
|
||||||
|
|
||||||
return new PhutilEmailAddress($address);
|
return new PhutilEmailAddress($address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function newVoidEmailAddress() {
|
||||||
|
return $this->newDefaultEmailAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Routing )------------------------------------------------------------ */
|
/* -( Routing )------------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
|
@ -161,10 +161,19 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
|
||||||
->setFilterMethod('isEnabled')
|
->setFilterMethod('isEnabled')
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
|
$targets = $this->newTargetAddresses();
|
||||||
|
foreach ($targets as $key => $target) {
|
||||||
|
// Never accept any reserved address as a mail target. This prevents
|
||||||
|
// security issues around "hostmaster@" and bad behavior with
|
||||||
|
// "noreply@".
|
||||||
|
if (PhabricatorMailUtil::isReservedAddress($target)) {
|
||||||
|
unset($targets[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$any_accepted = false;
|
$any_accepted = false;
|
||||||
$receiver_exception = null;
|
$receiver_exception = null;
|
||||||
|
|
||||||
$targets = $this->newTargetAddresses();
|
|
||||||
foreach ($receivers as $receiver) {
|
foreach ($receivers as $receiver) {
|
||||||
$receiver = id(clone $receiver)
|
$receiver = id(clone $receiver)
|
||||||
->setViewer($viewer);
|
->setViewer($viewer);
|
||||||
|
|
|
@ -62,4 +62,50 @@ final class PhabricatorMailUtil
|
||||||
return ($u->getAddress() === $v->getAddress());
|
return ($u->getAddress() === $v->getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function isReservedAddress(PhutilEmailAddress $address) {
|
||||||
|
$address = self::normalizeAddress($address);
|
||||||
|
$local = $address->getLocalPart();
|
||||||
|
|
||||||
|
$reserved = array(
|
||||||
|
'admin',
|
||||||
|
'administrator',
|
||||||
|
'hostmaster',
|
||||||
|
'list',
|
||||||
|
'list-request',
|
||||||
|
'majordomo',
|
||||||
|
'postmaster',
|
||||||
|
'root',
|
||||||
|
'ssl-admin',
|
||||||
|
'ssladmin',
|
||||||
|
'ssladministrator',
|
||||||
|
'sslwebmaster',
|
||||||
|
'sysadmin',
|
||||||
|
'uucp',
|
||||||
|
'webmaster',
|
||||||
|
|
||||||
|
'noreply',
|
||||||
|
'no-reply',
|
||||||
|
);
|
||||||
|
|
||||||
|
$reserved = array_fuse($reserved);
|
||||||
|
|
||||||
|
if (isset($reserved[$local])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$default_address = id(new PhabricatorMetaMTAMail())
|
||||||
|
->newDefaultEmailAddress();
|
||||||
|
if (self::matchAddresses($address, $default_address)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$void_address = id(new PhabricatorMetaMTAMail())
|
||||||
|
->newVoidEmailAddress();
|
||||||
|
if (self::matchAddresses($address, $void_address)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue