diff --git a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php --- a/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php +++ b/src/applications/config/option/PhabricatorMetaMTAConfigOptions.php @@ -191,10 +191,7 @@ $this->newOption('cluster.mailers', 'cluster.mailers', array()) ->setHidden(true) ->setDescription($mailers_description), - $this->newOption( - 'metamta.default-address', - 'string', - 'noreply@phabricator.example.com') + $this->newOption('metamta.default-address', 'string', null) ->setDescription(pht('Default "From" address.')), $this->newOption( 'metamta.one-mail-per-recipient', diff --git a/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php b/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php --- a/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php +++ b/src/applications/metamta/editor/PhabricatorMetaMTAApplicationEmailEditor.php @@ -104,6 +104,16 @@ pht('Invalid'), 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( diff --git a/src/applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php b/src/applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php --- a/src/applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php +++ b/src/applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php @@ -41,4 +41,30 @@ } } + 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" ' => 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)); + } + } + } diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php --- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php @@ -713,7 +713,7 @@ $actors = $this->loadAllActors(); $deliverable_actors = $this->filterDeliverableActors($actors); - $default_from = PhabricatorEnv::getEnvConfig('metamta.default-address'); + $default_from = (string)$this->newDefaultEmailAddress(); if (empty($params['from'])) { $mailer->setFrom($default_from); } @@ -1463,18 +1463,33 @@ } private function newMailDomain() { + $domain = PhabricatorEnv::getEnvConfig('metamta.reply-handler-domain'); + if (strlen($domain)) { + return $domain; + } + $install_uri = PhabricatorEnv::getURI('/'); $install_uri = new PhutilURI($install_uri); 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(); - $address = "void-recipient@{$domain}"; + $address = "noreply@{$domain}"; + return new PhutilEmailAddress($address); } + public function newVoidEmailAddress() { + return $this->newDefaultEmailAddress(); + } + /* -( Routing )------------------------------------------------------------ */ diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php --- a/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAReceivedMail.php @@ -161,10 +161,19 @@ ->setFilterMethod('isEnabled') ->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; $receiver_exception = null; - - $targets = $this->newTargetAddresses(); foreach ($receivers as $receiver) { $receiver = id(clone $receiver) ->setViewer($viewer); diff --git a/src/applications/metamta/util/PhabricatorMailUtil.php b/src/applications/metamta/util/PhabricatorMailUtil.php --- a/src/applications/metamta/util/PhabricatorMailUtil.php +++ b/src/applications/metamta/util/PhabricatorMailUtil.php @@ -62,4 +62,50 @@ 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; + } + }