From df71e97a09fa9b90b4766875ee20f9ba2345bcc3 Mon Sep 17 00:00:00 2001 From: Michael Kuron Date: Sat, 22 Apr 2017 12:33:53 +0200 Subject: [PATCH] Forwarding hosts: use SPF records if present --- data/web/admin.php | 35 +++++---- data/web/delete.php | 2 +- data/web/inc/functions.inc.php | 55 ++++++++++---- data/web/inc/init.sql | 1 + data/web/inc/spf.inc.php | 127 +++++++++++++++++++++++++++++++++ data/web/lang/lang.de.php | 13 +++- data/web/lang/lang.en.php | 5 +- 7 files changed, 206 insertions(+), 32 deletions(-) create mode 100644 data/web/inc/spf.inc.php diff --git a/data/web/admin.php b/data/web/admin.php index 6d0bdf82..defe794e 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -313,7 +313,8 @@ $tfa_data = get_tfa(); - + + @@ -321,20 +322,23 @@ $tfa_data = get_tfa(); - - - - - + foreach ($forwarding_hosts as $host) { + $source = $host->source; + $host = $host->host; + ?> + + + + + + - @@ -347,9 +351,10 @@ $tfa_data = get_tfa(); +

- +
diff --git a/data/web/delete.php b/data/web/delete.php index bfacad0c..bfeff0f3 100644 --- a/data/web/delete.php +++ b/data/web/delete.php @@ -105,7 +105,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm 'Database initialization completed.' ); } + // Add newly added tables + $stmt = $pdo->query("CREATE TABLE IF NOT EXISTS `forwarding_hosts` (`host` VARCHAR(255) NOT NULL, PRIMARY KEY (`host`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"); // Add newly added columns $stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'kind'"); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); @@ -124,9 +126,11 @@ function init_db_schema() { if ($num_results == 0) { $pdo->query("ALTER TABLE `tfa` ADD `key_id` VARCHAR(255) DEFAULT 'unidentified'"); } - - // Add newly added tables - $stmt = $pdo->query("CREATE TABLE IF NOT EXISTS `forwarding_hosts` (`host` VARCHAR(255) NOT NULL, PRIMARY KEY (`host`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"); + $stmt = $pdo->query("SHOW COLUMNS FROM `forwarding_hosts` LIKE 'source'"); + $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); + if ($num_results == 0) { + $pdo->query("ALTER TABLE `forwarding_hosts` ADD `source` VARCHAR(255) DEFAULT ''"); + } } function verify_ssha256($hash, $password) { // Remove tag if any @@ -5044,11 +5048,12 @@ function get_u2f_registrations($username) { } function get_forwarding_hosts() { global $pdo; - $sel = $pdo->prepare("SELECT host FROM `forwarding_hosts`"); + $sel = $pdo->prepare("SELECT host, source FROM `forwarding_hosts`"); $sel->execute(); - return $sel->fetchAll(PDO::FETCH_COLUMN); + return $sel->fetchAll(PDO::FETCH_OBJ); } function add_forwarding_host($postarray) { + require_once 'spf.inc.php'; global $pdo; global $lang; if ($_SESSION['mailcow_cc_role'] != "admin") { @@ -5058,23 +5063,47 @@ function add_forwarding_host($postarray) { ); return false; } + $source = $postarray['hostname']; $host = $postarray['hostname']; - try { - $stmt = $pdo->prepare("INSERT INTO `forwarding_hosts` (`host`) VALUES (:host)"); - $stmt->execute(array( - ':host' => $host, - )); + $hosts = array(); + if (preg_match('/^[0-9a-fA-F:\/]+$/', $host)) { // IPv6 address + $hosts = array($host); } - catch (PDOException $e) { + elseif (preg_match('/^[0-9\.\/]+$/', $host)) { // IPv4 address + $hosts = array($host); + } + else { + $hosts = get_outgoing_hosts_best_guess($host); + } + if (!$hosts) + { $_SESSION['return'] = array( 'type' => 'danger', - 'msg' => 'MySQL: '.$e + 'msg' => 'Invalid host specified: '. htmlspecialchars($host) ); return false; } + foreach ($hosts as $host) { + if ($source == $host) + $source = ''; + try { + $stmt = $pdo->prepare("INSERT IGNORE INTO `forwarding_hosts` (`host`, `source`) VALUES (:host, :source)"); + $stmt->execute(array( + ':host' => $host, + ':source' => $source, + )); + } + catch (PDOException $e) { + $_SESSION['return'] = array( + 'type' => 'danger', + 'msg' => 'MySQL: '.$e + ); + return false; + } + } $_SESSION['return'] = array( 'type' => 'success', - 'msg' => sprintf($lang['success']['forwarding_host_added'], htmlspecialchars($host)) + 'msg' => sprintf($lang['success']['forwarding_host_added'], htmlspecialchars(implode(', ', $hosts))) ); } function delete_forwarding_host($postarray) { diff --git a/data/web/inc/init.sql b/data/web/inc/init.sql index bdce3e9c..54b227c9 100644 --- a/data/web/inc/init.sql +++ b/data/web/inc/init.sql @@ -142,6 +142,7 @@ CREATE TABLE IF NOT EXISTS `tfa` ( CREATE TABLE IF NOT EXISTS `forwarding_hosts` ( `host` VARCHAR(255) NOT NULL, + `source` VARCHAR(255) NOT NULL, PRIMARY KEY (`host`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; diff --git a/data/web/inc/spf.inc.php b/data/web/inc/spf.inc.php new file mode 100644 index 00000000..0e584b40 --- /dev/null +++ b/data/web/inc/spf.inc.php @@ -0,0 +1,127 @@ + \ No newline at end of file diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index 07d23d70..a14b9143 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -450,11 +450,20 @@ $lang['admin']['unchanged_if_empty'] = 'Unverändert, wenn leer'; $lang['admin']['yes'] = '✔'; $lang['admin']['no'] = '✘'; $lang['admin']['access'] = 'Zugang'; -$lang['admin']['invalid_max_msg_size'] = 'Invalid max. message size'; // NEEDS TRANSLATION +$lang['admin']['invalid_max_msg_size'] = 'Ungültige maximale Nachrichtengröße'; $lang['admin']['site_not_found'] = 'Kann mailcow Site-Konfiguration nicht finden'; -$lang['admin']['public_folder_empty'] = 'Public folder name must not be empty'; // NEEDS TRANSLATION +$lang['admin']['public_folder_empty'] = 'Name des öffentlichen Ordners darf nicht leer sein'; $lang['admin']['set_rr_failed'] = 'Kann Postfix Restriktionen nicht setzen'; $lang['admin']['no_record'] = 'Kein Eintrag'; $lang['admin']['filter_table'] = 'Tabelle Filtern'; $lang['admin']['empty'] = 'Keine Einträge vorhanden'; +$lang['admin']['forwarding_hosts'] = 'Weiterleitungs-Hosts'; +$lang['admin']['forwarding_hosts_hint'] = 'Eingehende Nachrichten werden von den hier gelisteten Hosts bedingungslos akzeptiert. Diese Hosts werden dann nicht mit DNSBLs abgeglichen oder Greylisting unterworfen. Von ihnen empfangener Spam wird nie abgelehnt und immer in den Spam-Ordner einsortiert. Die übliche Verwendung für diese Funktion ist, um Mailserver anzugeben, auf denen eine Weiterleitung zu Ihrem Mailcow-Server eingerichtet wurde.'; +$lang['admin']['forwarding_hosts_add_hint'] = 'Sie können entweder IPv4/IPv6-Adressen, Netzwerke in CIDR-Notation, Hostnamen (die zu IP-Adressen aufgelöst werden), oder Domainnamen (die zu IP-Adressen aufgelöst werden, indem ihr SPF-Record abgefragt wird oder, in dessen Abwesenheit, ihre MX-Records) angeben.'; +$lang['edit']['host'] = 'Host'; +$lang['edit']['source'] = 'Quelle'; +$lang['admin']['add_forwarding_host'] = 'Weiterleitungs-Host hinzufügen'; +$lang['delete']['remove_forwardinghost_warning'] = 'Warnung: Sie entfernen den Weiterleitungs-Host %s!'; +$lang['success']['forwarding_host_removed'] = "Weiterleitungs-Host %s wurde entfernt"; +$lang['success']['forwarding_host_added'] = "Weiterleitungs-Host %s wurde hinzugefügt"; ?> diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php index 120f35cf..4bc48f2c 100644 --- a/data/web/lang/lang.en.php +++ b/data/web/lang/lang.en.php @@ -469,7 +469,10 @@ $lang['admin']['no_record'] = 'No record'; $lang['admin']['filter_table'] = 'Filter table'; $lang['admin']['empty'] = 'No results'; $lang['admin']['forwarding_hosts'] = 'Forwarding Hosts'; -$lang['admin']['forwarding_hosts_hint'] = 'Specify any networks (in CIDR notation) from which you unconditionally want to accept incoming messages. These hosts are then not checked against DNSBLs or subjected to greylisting. Spam received from them is never rejected and always filed into the Junk folder. The most common use for this is to specify mail servers on which you have set up a forwarding rule.'; +$lang['admin']['forwarding_hosts_hint'] = 'Incoming messages are unconditionally accepted from any hosts listed here. These hosts are then not checked against DNSBLs or subjected to greylisting. Spam received from them is never rejected and always filed into the Junk folder. The most common use for this is to specify mail servers on which you have set up a rule that forwards incoming emails to your Mailcow server.'; +$lang['admin']['forwarding_hosts_add_hint'] = 'You can either specify IPv4/IPv6 addresses, networks in CIDR notation, host names (which will be resolved to IP addresses), or domain names (which will be resolved to IP addresses by querying SPF records or, in their absence, MX records).'; +$lang['edit']['host'] = 'Host'; +$lang['edit']['source'] = 'Source'; $lang['admin']['add_forwarding_host'] = 'Add Forwarding Host'; $lang['delete']['remove_forwardinghost_warning'] = 'Warning: You are about to remove the forwarding host %s!'; $lang['success']['forwarding_host_removed'] = "Forwarding host %s has been removed";
-
- -
-
+
+ +
+