From b9ea02ce477d17d44eedd94d6fed3199569a472d Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 17 Oct 2013 17:02:23 +0200 Subject: [PATCH] DKIM-Modifications skip writing dkim-values for inactive domains removed the public-dkim-key from the interface added german language-file --- install/dist/lib/opensuse.lib.php | 2 +- interface/lib/classes/validate_dkim.inc.php | 93 +++++++++++ .../web/admin/form/server_config.tform.php | 8 +- .../web/admin/lib/lang/de_server_config.lng | 4 +- .../web/admin/lib/lang/en_server_config.lng | 2 + .../templates/server_config_mail_edit.htm | 6 +- interface/web/dns/dns_dkim_edit.php | 142 ++++++++++++++++ interface/web/dns/dns_dkim_get.php | 99 +++++++++++ interface/web/dns/dns_wizard.php | 9 +- interface/web/dns/form/dns_dkim.tform.php | 154 ++++++++++++++++++ interface/web/dns/form/dns_template.tform.php | 9 +- interface/web/dns/lib/lang/de_dns_wizard.lng | 1 - interface/web/dns/lib/lang/en_dns_dkim.lng | 8 + interface/web/dns/lib/lang/en_dns_wizard.lng | 3 +- interface/web/dns/templates/dns_a_list.htm | 1 + interface/web/dns/templates/dns_dkim_edit.htm | 38 +++++ interface/web/dns/templates/dns_wizard.htm | 5 + interface/web/js/dns_dkim.js | 73 +++++++++ interface/web/js/mail_domain_dkim.js | 72 ++++++++ interface/web/mail/form/mail_domain.tform.php | 29 +++- .../web/mail/lib/lang/de_mail_domain.lng | 1 - .../web/mail/lib/lang/en_mail_domain.lng | 9 +- .../web/mail/mail_domain_dkim_create.php | 103 ++++++++++++ .../web/mail/templates/mail_domain_edit.htm | 4 + .../mail_plugin_dkim.inc.php | 106 +++++++----- 25 files changed, 927 insertions(+), 54 deletions(-) create mode 100644 interface/lib/classes/validate_dkim.inc.php create mode 100644 interface/web/dns/dns_dkim_edit.php create mode 100644 interface/web/dns/dns_dkim_get.php create mode 100644 interface/web/dns/form/dns_dkim.tform.php create mode 100644 interface/web/dns/lib/lang/en_dns_dkim.lng create mode 100644 interface/web/dns/templates/dns_dkim_edit.htm create mode 100644 interface/web/js/dns_dkim.js create mode 100644 interface/web/js/mail_domain_dkim.js create mode 100644 interface/web/mail/mail_domain_dkim_create.php diff --git a/install/dist/lib/opensuse.lib.php b/install/dist/lib/opensuse.lib.php index ff71aea837..1e536abd64 100644 --- a/install/dist/lib/opensuse.lib.php +++ b/install/dist/lib/opensuse.lib.php @@ -265,7 +265,7 @@ class installer_dist extends installer_base { $command = 'chmod 755 /var/run/authdaemon.courier-imap'; caselog($command.' &> /dev/null', __FILE__, __LINE__, 'EXECUTED: '.$command, 'Failed to execute the command '.$command); - //* Changing maildrop lines in posfix master.cf + //* Changing maildrop lines in posfix master.cf if(is_file($config_dir.'/master.cf')){ copy($config_dir.'/master.cf', $config_dir.'/master.cf~'); } diff --git a/interface/lib/classes/validate_dkim.inc.php b/interface/lib/classes/validate_dkim.inc.php new file mode 100644 index 0000000000..e633430cf3 --- /dev/null +++ b/interface/lib/classes/validate_dkim.inc.php @@ -0,0 +1,93 @@ +tform->wordbook[$errmsg])) { + return $app->tform->wordbook[$errmsg]."
\r\n"; + } else { + return $errmsg."
\r\n"; + } + } + + /** + * Validator function for private DKIM-Key + */ + function check_private_key($field_name, $field_value, $validator) { + $dkim_enabled=$_POST['dkim']; + if ($dkim_enabled == 'y') { + if (empty($field_value)) return $this->get_error($validator['errmsg']); + exec('echo '.escapeshellarg($field_value).'|openssl rsa -check',$output,$result); + if($result != 0) return $this->get_error($validator['errmsg']); + } + } + + /** + * Validator function for DKIM Path + * @return boolean - true when the dkim-path exists and is writeable + */ + function check_dkim_path($field_name, $field_value, $validator) { + if(empty($field_value)) return $this->get_error($validator['errmsg']); + if (substr(sprintf('%o', fileperms($field_value)),-3) <= 600) + return $this->get_error($validator['errmsg']); + } + + /** + * Check function for DNS-Template + */ + function check_template($field_name, $field_value, $validator) { + $dkim=false; + foreach($field_value as $field ) { if($field == 'DKIM') $dkim=true; } + if ($dkim && $field_value[0]!='DOMAIN') return $this->get_error($validator['errmsg']); + } + + /** + * Validator function for $_POST + * + * @return boolean - true if $POST contains a real key-file + */ + function validate_post($key,$value) { + switch ($key) { + case 'public': + if (preg_match("/(^-----BEGIN PUBLIC KEY-----)[a-zA-Z0-9\r\n\/\+=]{1,221}(-----END PUBLIC KEY-----(\n|\r)$)/",$value) === 1) { return true; } else { return false; } + break; + case 'private': + if (preg_match("/(^-----BEGIN RSA PRIVATE KEY-----)[a-zA-Z0-9\r\n\/\+=]{1,850}(-----END RSA PRIVATE KEY-----(\n|\r)$)/",$value) === 1) { return true; } else { return false; } + break; + } + } +} + diff --git a/interface/web/admin/form/server_config.tform.php b/interface/web/admin/form/server_config.tform.php index 298894ad80..2a00f99983 100644 --- a/interface/web/admin/form/server_config.tform.php +++ b/interface/web/admin/form/server_config.tform.php @@ -282,10 +282,10 @@ $form["tabs"]['mail'] = array( 'formtype' => 'TEXT', 'default' => '/var/lib/amavis/dkim', 'validators' => array ( 0 => array ('type' => 'CUSTOM', - 'class' => 'validate_dkim', - 'function' => 'check_dkim_path', - 'errmsg'=> 'dkim_path_error'), - ), + 'class' => 'validate_dkim', + 'function' => 'check_dkim_path', + 'errmsg'=> 'dkim_path_error'), + ), 'value' => '', 'width' => '40', 'maxlength' => '255' diff --git a/interface/web/admin/lib/lang/de_server_config.lng b/interface/web/admin/lib/lang/de_server_config.lng index 327f4e243b..b5fdddd037 100644 --- a/interface/web/admin/lib/lang/de_server_config.lng +++ b/interface/web/admin/lib/lang/de_server_config.lng @@ -19,8 +19,8 @@ $wb['fastcgi_bin_txt'] = 'FastCGI Bin'; $wb['module_txt'] = 'Modul'; $wb['maildir_path_txt'] = 'Maildir Pfad'; $wb['homedir_path_txt'] = 'Homedir Pfad'; -$wb["dkim_path_txt"] = 'DKIM Pfad'; -$wb["dkim_path_error"] = 'DKIM Pfad nicht gefunden oder nicht beschreibbar.'; +$wb['dkim_path_txt'] = 'DKIM Pfad'; +$wb['dkim_path_error'] = 'DKIM Pfad nicht gefunden oder nicht beschreibbar.'; $wb['mailuser_uid_txt'] = 'Mailbenutzer UID'; $wb['mailuser_gid_txt'] = 'Mailbenutzer GID'; $wb['mailuser_name_txt'] = 'Mailbenutzer Name'; diff --git a/interface/web/admin/lib/lang/en_server_config.lng b/interface/web/admin/lib/lang/en_server_config.lng index 36a6791ad7..21183efbc3 100644 --- a/interface/web/admin/lib/lang/en_server_config.lng +++ b/interface/web/admin/lib/lang/en_server_config.lng @@ -30,6 +30,8 @@ $wb["fastcgi_bin_txt"] = 'FastCGI Bin'; $wb["module_txt"] = 'Module'; $wb["maildir_path_txt"] = 'Maildir Path'; $wb["homedir_path_txt"] = 'Homedir Path'; +$wb["dkim_path_txt"] = 'DKIM Path'; +$wb["dkim_path_error"] = 'DKIM Path not found or not writeable.'; $wb["mailuser_uid_txt"] = 'Mailuser UID'; $wb["mailuser_gid_txt"] = 'Mailuser GID'; $wb["mailuser_name_txt"] = 'Mailuser Name'; diff --git a/interface/web/admin/templates/server_config_mail_edit.htm b/interface/web/admin/templates/server_config_mail_edit.htm index 752bc22da2..6ab03eb9bd 100644 --- a/interface/web/admin/templates/server_config_mail_edit.htm +++ b/interface/web/admin/templates/server_config_mail_edit.htm @@ -19,6 +19,10 @@ +
+ + +

{tmpl_var name='pop3_imap_daemon_txt'}

@@ -113,4 +117,4 @@
- \ No newline at end of file + diff --git a/interface/web/dns/dns_dkim_edit.php b/interface/web/dns/dns_dkim_edit.php new file mode 100644 index 0000000000..2395bb01d9 --- /dev/null +++ b/interface/web/dns/dns_dkim_edit.php @@ -0,0 +1,142 @@ +auth->check_module_permissions('dns'); + +// Loading classes +$app->uses('tpl,tform,tform_actions,validate_dns'); +$app->load('tform_actions'); + +class page_action extends tform_actions { + + function onShowNew() { + global $app, $conf; + // we will check only users, not admins + if($_SESSION["s"]["user"]["typ"] == 'user') { + + // Get the limits of the client + $client_group_id = $_SESSION["s"]["user"]["default_group"]; + $client = $app->db->queryOneRecord("SELECT limit_dns_record FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id"); + + // Check if the user may add another record. + if($client["limit_dns_record"] >= 0) { + $tmp = $app->db->queryOneRecord("SELECT count(id) as number FROM dns_rr WHERE sys_groupid = $client_group_id"); + if($tmp["number"] >= $client["limit_dns_record"]) { + $app->error($app->tform->wordbook["limit_dns_record_txt"]); + } + } + } + + parent::onShowNew(); + } + + function onSubmit() { + global $app, $conf; + // Get the parent soa record of the domain + $soa = $app->db->queryOneRecord("SELECT * FROM dns_soa WHERE id = '".$app->functions->intval($_POST["zone"])."' AND ".$app->tform->getAuthSQL('r')); + // Check if Domain belongs to user + if($soa["id"] != $_POST["zone"]) $app->tform->errorMessage .= $app->tform->wordbook["no_zone_perm"]; + + // Check the client limits, if user is not the admin + if($_SESSION["s"]["user"]["typ"] != 'admin') { // if user is not admin + // Get the limits of the client + $client_group_id = $_SESSION["s"]["user"]["default_group"]; + $client = $app->db->queryOneRecord("SELECT limit_dns_record FROM sys_group, client WHERE sys_group.client_id = client.client_id and sys_group.groupid = $client_group_id"); + // Check if the user may add another record. + if($this->id == 0 && $client["limit_dns_record"] >= 0) { + $tmp = $app->db->queryOneRecord("SELECT count(id) as number FROM dns_rr WHERE sys_groupid = $client_group_id"); + if($tmp["number"] >= $client["limit_dns_record"]) { + $app->error($app->tform->wordbook["limit_dns_record_txt"]); + } + } + } // end if user is not admin + + // Set the server ID of the rr record to the same server ID as the parent record. + $this->dataRecord["server_id"] = $soa["server_id"]; + + // add dkim-settings to the public-key in the txt-record + $this->dataRecord['data']='v=DKIM1; t=s; p='.$this->dataRecord['data']; + $this->dataRecord['name']='default._domainkey.'.$this->dataRecord['name']; + + // Update the serial number and timestamp of the RR record + $soa = $app->db->queryOneRecord("SELECT serial FROM dns_rr WHERE id = ".$this->id); + $this->dataRecord["serial"] = $app->validate_dns->increase_serial($soa["serial"]); + $this->dataRecord["stamp"] = date('Y-m-d H:i:s'); + + // check for duplicate entry + $check=$app->db->queryOneRecord("SELECT * FROM dns_rr WHERE zone = ".$this->dataRecord["zone"]." AND type = '".$this->dataRecord["type"]."' AND data ='".$this->dataRecord["data"]."' AND name = '".$this->dataRecord['name']."'"); + if ($check!='') $app->tform->errorMessage .= $app->tform->wordbook["record_exists_txt"]; + + parent::onSubmit(); + } + + function onAfterInsert() { + global $app, $conf; + + //* Set the sys_groupid of the rr record to be the same then the sys_groupid of the soa record + $soa = $app->db->queryOneRecord("SELECT sys_groupid,serial FROM dns_soa WHERE id = '".$app->functions->intval($this->dataRecord["zone"])."' AND ".$app->tform->getAuthSQL('r')); + $app->db->datalogUpdate('dns_rr', "sys_groupid = ".$soa['sys_groupid'], 'id', $this->id); + + //* Update the serial number of the SOA record + $soa_id = $app->functions->intval($_POST["zone"]); + $serial = $app->validate_dns->increase_serial($soa["serial"]); + $app->db->datalogUpdate('dns_soa', "serial = $serial", 'id', $soa_id); + } + + function onAfterUpdate() { + global $app, $conf; + + //* Update the serial number of the SOA record + $soa = $app->db->queryOneRecord("SELECT serial FROM dns_soa WHERE id = '".$app->functions->intval($this->dataRecord["zone"])."' AND ".$app->tform->getAuthSQL('r')); + $soa_id = $app->functions->intval($_POST["zone"]); + $serial = $app->validate_dns->increase_serial($soa["serial"]); + $app->db->datalogUpdate('dns_soa', "serial = $serial", 'id', $soa_id); + } +} + +$page = new page_action; +$page->onLoad(); + +?> diff --git a/interface/web/dns/dns_dkim_get.php b/interface/web/dns/dns_dkim_get.php new file mode 100644 index 0000000000..da917b96b3 --- /dev/null +++ b/interface/web/dns/dns_dkim_get.php @@ -0,0 +1,99 @@ +auth->check_module_permissions('dns'); + +global $app, $conf; + +// Loading classes +$app->uses('tform,tform_actions'); + +header('Content-Type: text/xml; charset=utf-8'); +header('Cache-Control: must-revalidate, pre-check=0, no-store, no-cache, max-age=0, post-check=0'); + +/** +* This function fix PHP's messing up POST input containing characters space, dot, +* open square bracket and others to be compatible with with the deprecated register_globals +* @return array POST +*/ + +function getRealPOST() { + $pairs = explode("&", file_get_contents("php://input")); + $vars = array(); + foreach ($pairs as $pair) { + $nv = explode("=", $pair, 2); + $name = urldecode($nv[0]); + $value = $nv[1]; + $vars[$name] = $value; + } + return $vars; +} + +/** +* This function formats the public-key +* @param array $pubkey +* @return string public-key +*/ +function pub_key($pubkey) { + $public_key=''; + foreach($pubkey as $values) $public_key=$public_key.$values; + return $public_key; +} + +$_POST=getRealPost(); + +if (ctype_digit($_POST['zone'])) { + // Get the parent soa record of the domain + $soa = $app->db->queryOneRecord("SELECT * FROM dns_soa WHERE id = '".$app->db->quote($_POST['zone'])."' AND ".$app->tform->getAuthSQL('r')); + + $public_key=$app->db->queryOneRecord("SELECT dkim_public FROM mail_domain WHERE domain = '".substr_replace($soa['origin'],'',-1)."' AND ".$app->tform->getAuthSQL('r')); + + $public_key=pub_key($public_key); + + $public_key=str_replace(array('-----BEGIN PUBLIC KEY-----','-----END PUBLIC KEY-----',"\r","\n"),'',$public_key); + + echo "\n"; + echo "\n"; + echo "".$public_key."\n"; + echo "".$soa['origin']."\n"; + echo "\n"; +} +?> diff --git a/interface/web/dns/dns_wizard.php b/interface/web/dns/dns_wizard.php index ad26c431d1..10cfd9a757 100644 --- a/interface/web/dns/dns_wizard.php +++ b/interface/web/dns/dns_wizard.php @@ -195,6 +195,13 @@ if($_POST['create'] == 1) { if($_POST['ns1'] != '') $tpl_content = str_replace('{NS1}',$_POST['ns1'],$tpl_content); if($_POST['ns2'] != '') $tpl_content = str_replace('{NS2}',$_POST['ns2'],$tpl_content); if($_POST['email'] != '') $tpl_content = str_replace('{EMAIL}',$_POST['email'],$tpl_content); + if(isset($_POST['dkim']) && preg_match('/^[\w\.\-\/]{2,255}\.[a-zA-Z0-9\-]{2,30}[\.]{0,1}$/',$_POST['domain'])) { + $public_key=$app->db->queryOneRecord("SELECT dkim_public FROM mail_domain WHERE domain = '".$app->db->quote($_POST['domain'])."' AND dkim = 'y' AND ".$app->tform->getAuthSQL('r')); + if ($public_key!='') { + $dns_record=str_replace(array("\r\n", "\n", "\r","-----BEGIN PUBLIC KEY-----","-----END PUBLIC KEY-----"),'',$public_key['dkim_public']); + $tpl_content = str_replace('{DKIM}','TXT|default._domainkey.'.$_POST['domain'].'.|v=DKIM1; t=s; p='.$dns_record,$tpl_content); + } + } // Parse the template $tpl_rows = explode("\n",$tpl_content); @@ -295,4 +302,4 @@ $app->tpl_defaults(); $app->tpl->pparse(); -?> \ No newline at end of file +?> diff --git a/interface/web/dns/form/dns_dkim.tform.php b/interface/web/dns/form/dns_dkim.tform.php new file mode 100644 index 0000000000..71607c765e --- /dev/null +++ b/interface/web/dns/form/dns_dkim.tform.php @@ -0,0 +1,154 @@ + 0 id must match with id of current user +$form["auth_preset"]["groupid"] = 0; // 0 = default groupid of the user, > 0 id must match with groupid of current user +$form["auth_preset"]["perm_user"] = 'riud'; //r = read, i = insert, u = update, d = delete +$form["auth_preset"]["perm_group"] = 'riud'; //r = read, i = insert, u = update, d = delete +$form["auth_preset"]["perm_other"] = ''; //r = read, i = insert, u = update, d = delete + +$form["tabs"]['dns'] = array ( + 'title' => "DNS DKIM", + 'width' => 100, + 'template' => "templates/dns_dkim_edit.htm", + 'fields' => array ( + ################################## + # Begin Datatable fields + ################################## + 'server_id' => array ( + 'datatype' => 'INTEGER', + 'formtype' => 'SELECT', + 'default' => '', + 'value' => '', + 'width' => '30', + 'maxlength' => '255' + ), + 'zone' => array ( + 'datatype' => 'INTEGER', + 'formtype' => 'TEXT', + 'default' => @$app->functions->intval($_REQUEST["zone"]), + 'value' => '', + 'width' => '30', + 'maxlength' => '255' + ), + 'name' => array ( + 'datatype' => 'VARCHAR', + 'formtype' => 'TEXT', + 'filters' => array( 0 => array( 'event' => 'SAVE', + 'type' => 'IDNTOASCII'), + 1 => array( 'event' => 'SHOW', + 'type' => 'IDNTOUTF8'), + 2 => array( 'event' => 'SAVE', + 'type' => 'TOLOWER') + ), + 'validators' => array ( 0 => array ( 'type' => 'REGEX', + 'regex' => '/^[\w\.\-]{0,255}$/', + 'errmsg'=> 'name_error_regex'), + ), + 'default' => '', + 'value' => '', + 'width' => '30', + 'maxlength' => '255' + ), + 'type' => array ( + 'datatype' => 'VARCHAR', + 'formtype' => 'TEXT', + 'default' => 'TXT', + 'value' => '', + 'width' => '5', + 'maxlength' => '5' + ), + 'data' => array ( + 'datatype' => 'VARCHAR', + 'formtype' => 'TEXT', + 'validators' => array ( 0 => array ( 'type' => 'NOTEMPTY', + 'errmsg'=> 'data_error_empty'), + ), + 'default' => '', + 'value' => '', + 'width' => '30', + 'maxlength' => '255' + ), + 'ttl' => array ( + 'datatype' => 'INTEGER', + 'formtype' => 'TEXT', + 'default' => '86400', + 'value' => '', + 'width' => '10', + 'maxlength' => '10' + ), + 'active' => array ( + 'datatype' => 'VARCHAR', + 'formtype' => 'CHECKBOX', + 'default' => 'Y', + 'value' => array(0 => 'N',1 => 'Y') + ), + 'stamp' => array ( + 'datatype' => 'VARCHAR', + 'formtype' => 'TEXT', + 'default' => '', + 'value' => '', + 'width' => '30', + 'maxlength' => '255' + ), + 'serial' => array ( + 'datatype' => 'INTEGER', + 'formtype' => 'TEXT', + 'default' => '', + 'value' => '', + 'width' => '10', + 'maxlength' => '10' + ), + ################################## + # ENDE Datatable fields + ################################## + ) +); + + + +?> diff --git a/interface/web/dns/form/dns_template.tform.php b/interface/web/dns/form/dns_template.tform.php index 1f020da04c..3822719358 100644 --- a/interface/web/dns/form/dns_template.tform.php +++ b/interface/web/dns/form/dns_template.tform.php @@ -74,7 +74,12 @@ $form["tabs"]['template'] = array ( 'formtype' => 'CHECKBOXARRAY', 'default' => '', 'separator' => ',', - 'value' => array('DOMAIN' => 'Domain','IP' => 'IP Address','NS1' => 'NS 1','NS2' => 'NS 2','EMAIL' => 'Email') + 'value' => array('DOMAIN' => 'Domain','IP' => 'IP Address','NS1' => 'NS 1','NS2' => 'NS 2','EMAIL' => 'Email', 'DKIM' => 'DKIM (use {DKIM}|0|3600 in your Template)'), + 'validators' => array ( 0 => array ('type' => 'CUSTOM', + 'class' => 'validate_dkim', + 'function' => 'check_template', + 'errmsg'=> 'dkim_domain_error'), + ), ), 'template' => array ( 'datatype' => 'TEXT', @@ -98,4 +103,4 @@ $form["tabs"]['template'] = array ( -?> \ No newline at end of file +?> diff --git a/interface/web/dns/lib/lang/de_dns_wizard.lng b/interface/web/dns/lib/lang/de_dns_wizard.lng index 4b00991403..5a8158b16d 100644 --- a/interface/web/dns/lib/lang/de_dns_wizard.lng +++ b/interface/web/dns/lib/lang/de_dns_wizard.lng @@ -37,4 +37,3 @@ $wb['globalsearch_noresults_limit_txt'] = '0 Treffer'; $wb['globalsearch_searchfield_watermark_txt'] = 'Suche'; $wb['globalsearch_suggestions_text_txt'] = 'Vorschläge'; ?> - diff --git a/interface/web/dns/lib/lang/en_dns_dkim.lng b/interface/web/dns/lib/lang/en_dns_dkim.lng new file mode 100644 index 0000000000..dabee2bd11 --- /dev/null +++ b/interface/web/dns/lib/lang/en_dns_dkim.lng @@ -0,0 +1,8 @@ + diff --git a/interface/web/dns/lib/lang/en_dns_wizard.lng b/interface/web/dns/lib/lang/en_dns_wizard.lng index f7057b45c7..99f1f2aa52 100644 --- a/interface/web/dns/lib/lang/en_dns_wizard.lng +++ b/interface/web/dns/lib/lang/en_dns_wizard.lng @@ -9,6 +9,7 @@ $wb["btn_save_txt"] = 'Create DNS-Record'; $wb["btn_cancel_txt"] = 'Cancel'; $wb['domain_txt'] = 'Domain'; $wb['email_txt'] = 'Email'; +$wb['dkim_txt'] = 'DKIM enabled'; $wb['ns1_txt'] = 'NS 1'; $wb['ns2_txt'] = 'NS 2'; $wb['ip_txt'] = 'IP Address'; @@ -35,4 +36,4 @@ $wb['globalsearch_noresults_text_txt'] = "No results."; $wb['globalsearch_noresults_limit_txt'] = "0 results"; $wb['globalsearch_searchfield_watermark_txt'] = "Search"; $wb['globalsearch_suggestions_text_txt'] = "Suggestions"; -?> \ No newline at end of file +?> diff --git a/interface/web/dns/templates/dns_a_list.htm b/interface/web/dns/templates/dns_a_list.htm index 549f0c345a..14a5689ae0 100644 --- a/interface/web/dns/templates/dns_a_list.htm +++ b/interface/web/dns/templates/dns_a_list.htm @@ -21,6 +21,7 @@ + diff --git a/interface/web/dns/templates/dns_dkim_edit.htm b/interface/web/dns/templates/dns_dkim_edit.htm new file mode 100644 index 0000000000..3f89320854 --- /dev/null +++ b/interface/web/dns/templates/dns_dkim_edit.htm @@ -0,0 +1,38 @@ +

+

+ +
+
+
+
+ + +
+
+ + +
+ +
+

{tmpl_var name='active_txt'}

+
+ {tmpl_var name='active'} +
+
+
+ + + + + +
+ +
+ + +
+
+ + + + diff --git a/interface/web/dns/templates/dns_wizard.htm b/interface/web/dns/templates/dns_wizard.htm index 849c5b4e15..da2c91815f 100644 --- a/interface/web/dns/templates/dns_wizard.htm +++ b/interface/web/dns/templates/dns_wizard.htm @@ -67,6 +67,11 @@ + + + + + diff --git a/interface/web/js/dns_dkim.js b/interface/web/js/dns_dkim.js new file mode 100644 index 0000000000..817fe32bd0 --- /dev/null +++ b/interface/web/js/dns_dkim.js @@ -0,0 +1,73 @@ +/* +Copyright (c) 2007 - 2013, Till Brehm, projektfarm Gmbh +Copyright (c) 2013, Florian Schaal, info@schaal-24.de +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of ISPConfig nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +This Javascript is invoked by + * dns/templates/dns_dkim_edit.htm to get the public key +*/ + var request = false; + + function setRequest(zone) { + if (window.XMLHttpRequest) {request = new XMLHttpRequest();} + else if (window.ActiveXObject) { + try {request = new ActiveXObject('Msxml2.XMLHTTP');} + catch (e) { + try {request = new ActiveXObject('Microsoft.XMLHTTP');} + catch (e) {} + } + } + if (!request) { + alert("Error creating XMLHTTP-instance"); + return false; + } else { + request.open('POST', 'dns/dns_dkim_get.php', true); + request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + request.send('&zone='+zone); + request.onreadystatechange = interpretRequest; + } + } + + function interpretRequest() { + switch (request.readyState) { + case 4: + if (request.status != 200) {alert("Request done but NOK\nError:"+request.status);} + else { + document.getElementsByName('data')[0].value = request.responseXML.getElementsByTagName('data')[0].firstChild.nodeValue; + document.getElementsByName('name')[0].value = request.responseXML.getElementsByTagName('name')[0].firstChild.nodeValue; + } + break; + default: + break; + } + } + +var serverType = jQuery('#zone').val(); +setRequest(serverType); + + diff --git a/interface/web/js/mail_domain_dkim.js b/interface/web/js/mail_domain_dkim.js new file mode 100644 index 0000000000..aaace2e047 --- /dev/null +++ b/interface/web/js/mail_domain_dkim.js @@ -0,0 +1,72 @@ +/* +Copyright (c) 2007 - 2013, Till Brehm, projektfarm Gmbh +Copyright (c) 2013, Florian Schaal, info@schaal-24.de +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of ISPConfig nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +This Javascript is invoked by + * mail/templates/mail_domain_edit.htm to show and/or create the key-pair +*/ + var request = false; + + function setRequest(action,value,privatekey) { + if (window.XMLHttpRequest) {request = new XMLHttpRequest();} + else if (window.ActiveXObject) { + try {request = new ActiveXObject('Msxml2.XMLHTTP');} + catch (e) { + try {request = new ActiveXObject('Microsoft.XMLHTTP');} + catch (e) {} + } + } + if (!request) { + alert("Error creating XMLHTTP-instance"); + return false; + } else { + request.open('POST', 'mail/mail_domain_dkim_create.php', true); + request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + request.send('domain='+value+'&action='+action+'&pkey='+privatekey); + request.onreadystatechange = interpretRequest; + } + } + + function interpretRequest() { + switch (request.readyState) { + case 4: + if (request.status != 200) {alert("Request done but NOK\nError:"+request.status);} + else { + document.getElementsByName('dkim_private')[0].value = request.responseXML.getElementsByTagName('privatekey')[0].firstChild.nodeValue; + document.getElementsByName('dkim_public')[0].value = request.responseXML.getElementsByTagName('publickey')[0].firstChild.nodeValue; + } + break; + default: + break; + } + } + +var serverType = jQuery('#dkim_private').val(); +setRequest('show','{tmpl_var name="domain"}',serverType); + diff --git a/interface/web/mail/form/mail_domain.tform.php b/interface/web/mail/form/mail_domain.tform.php index 506f934e38..caf48d50b3 100644 --- a/interface/web/mail/form/mail_domain.tform.php +++ b/interface/web/mail/form/mail_domain.tform.php @@ -98,6 +98,33 @@ $form["tabs"]['domain'] = array ( 'maxlength' => '255', 'searchable' => 1 ), + 'dkim' => array ( + 'datatype' => 'VARCHAR', + 'formtype' => 'CHECKBOX', + 'default' => 'n', + 'value' => array(0 => 'n',1 => 'y') + ), + 'dkim_private' => array ( + 'datatype' => 'TEXT', + 'formtype' => 'TEXTAREA', + 'default' => '', + 'value' => '', + 'cols' => '30', + 'rows' => '10', + 'validators' => array ( 0 => array ('type' => 'CUSTOM', + 'class' => 'validate_dkim', + 'function' => 'check_private_key', + 'errmsg'=> 'dkim_private_key_error'), + ), + ), + 'dkim_public' => array ( + 'datatype' => 'TEXT', + 'formtype' => 'TEXTAREA', + 'default' => '', + 'value' => '', + 'cols' => '30', + 'rows' => '10' + ), 'active' => array ( 'datatype' => 'VARCHAR', 'formtype' => 'CHECKBOX', @@ -111,4 +138,4 @@ $form["tabs"]['domain'] = array ( ); -?> \ No newline at end of file +?> diff --git a/interface/web/mail/lib/lang/de_mail_domain.lng b/interface/web/mail/lib/lang/de_mail_domain.lng index ae0c822eb9..3d1b150565 100644 --- a/interface/web/mail/lib/lang/de_mail_domain.lng +++ b/interface/web/mail/lib/lang/de_mail_domain.lng @@ -17,4 +17,3 @@ $wb['limit_maildomain_txt'] = 'Die maximale Anzahl an E-Mail Domains für Ihr Ko $wb['policy_txt'] = 'Spamfilter'; $wb['no_policy'] = '- nicht aktiviert -'; ?> - diff --git a/interface/web/mail/lib/lang/en_mail_domain.lng b/interface/web/mail/lib/lang/en_mail_domain.lng index ad93100696..8d33cc0a63 100644 --- a/interface/web/mail/lib/lang/en_mail_domain.lng +++ b/interface/web/mail/lib/lang/en_mail_domain.lng @@ -3,11 +3,18 @@ $wb["server_id_txt"] = 'Server'; $wb["domain_txt"] = 'Domain'; $wb["type_txt"] = 'Type'; $wb["active_txt"] = 'Active'; +$wb["dkim_txt"] = 'enable DKIM'; +$wb["dkim_private_txt"] = 'DKIM Private-key'; +$wb["dkim_public_txt"] = 'DKIM Public-key\nfor information only'; +$wb["dkim_generate_txt"] = 'Generate DKIM Private-key'; +$wb["dkim_dns_txt"] = 'DNS-Record (TYPE TXT)

add this record to your DNS'; +$wb["dkim_private_key_error"] = 'Invalid DKIM-Private key'; $wb["domain_error_empty"] = 'Domain is empty.'; $wb["domain_error_unique"] = 'Duplicate Domain.'; $wb["domain_error_regex"] = 'Invalid domain name.'; +$wb["dkim_settings_txt"] = 'DomainKeys Identified Mail (DKIM)'; $wb["client_txt"] = 'Client'; $wb["limit_maildomain_txt"] = 'The max. number of email domains for your account is reached.'; $wb["policy_txt"] = 'Spamfilter'; $wb["no_policy"] = '- not enabled -'; -?> \ No newline at end of file +?> diff --git a/interface/web/mail/mail_domain_dkim_create.php b/interface/web/mail/mail_domain_dkim_create.php new file mode 100644 index 0000000000..495c8df274 --- /dev/null +++ b/interface/web/mail/mail_domain_dkim_create.php @@ -0,0 +1,103 @@ +auth->check_module_permissions('mail'); + +header('Content-Type: text/xml; charset=utf-8'); +header('Cache-Control: must-revalidate, pre-check=0, no-store, no-cache, max-age=0, post-check=0'); + +/** +* This function fix PHP's messing up POST input containing characters space, dot, +* open square bracket and others to be compatible with with the deprecated register_globals +* @return array POST +*/ +function getRealPOST() { + $pairs = explode("&", file_get_contents("php://input")); + $vars = array(); + foreach ($pairs as $pair) { + $nv = explode("=", $pair, 2); + $name = urldecode($nv[0]); + $value = $nv[1]; + $vars[$name] = $value; + } + return $vars; +} + +/** +* This function formats the public-key +* @param array $pubkey +* @return string public-key +*/ +function pub_key($pubkey) { + $public_key=''; + foreach($pubkey as $values) $public_key=$public_key.$values."\n"; + return $public_key; +} +$_POST=getRealPOST(); + +switch ($_POST['action']) { + case 'create': /* create DKIM Private-key */ + exec('openssl rand -out /usr/local/ispconfig/server/temp/random-data.bin 4096',$output,$result); + exec('openssl genrsa -rand /usr/local/ispconfig/server/temp/random-data.bin 1024',$privkey,$result); + unlink("/usr/local/ispconfig/server/temp/random-data.bin"); + $private_key=''; + foreach($privkey as $values) $private_key=$private_key.$values."\n"; + if($validate_dkim->validate_post('private',$private_key)) { /* validate the $_POST-value */ + exec('echo '.escapeshellarg($private_key).'|openssl rsa -pubout -outform PEM',$pubkey,$result); + $public_key=pub_key($pubkey); + } else { $public_key='invalid key'; } + break; + case 'show': /* show the DNS-Record onLoad */ + $private_key=$_POST['pkey']; + if($validate_dkim->validate_post('private',$private_key)) { /* validate the $_POST-value */ + /* get the public-key */ + exec('echo '.escapeshellarg($private_key).'|openssl rsa -pubout -outform PEM',$pubkey,$result); + $public_key=pub_key($pubkey); + } else { $public_key='invalid key'; } + break; +} +echo "\n"; +echo "\n"; +echo "".$private_key."\n"; +echo "".$public_key."\n"; +echo "\n"; +?> diff --git a/interface/web/mail/templates/mail_domain_edit.htm b/interface/web/mail/templates/mail_domain_edit.htm index 703515b87a..ed9d5ea6eb 100644 --- a/interface/web/mail/templates/mail_domain_edit.htm +++ b/interface/web/mail/templates/mail_domain_edit.htm @@ -74,6 +74,10 @@
+
diff --git a/server/plugins-available/mail_plugin_dkim.inc.php b/server/plugins-available/mail_plugin_dkim.inc.php index 8d813398e2..72a1d03ee5 100644 --- a/server/plugins-available/mail_plugin_dkim.inc.php +++ b/server/plugins-available/mail_plugin_dkim.inc.php @@ -1,6 +1,6 @@ plugins->registerEvent('mail_domain_update',$this->plugin_name,'domain_dkim_update'); } - /* - This function gets the amavisd-config file + /** + * This function gets the amavisd-config file + * @return string path to the amavisd-config for dkim-keys */ function get_amavis_config() { $pos_config=array( @@ -84,9 +88,11 @@ class mail_plugin_dkim { return $amavis_configfile; } - /* - This function checks the relevant configs and disables dkim for the domain - if the directory for dkim is not writeable or does not exist + /** + * This function checks the relevant configs and disables dkim for the domain + * if the directory for dkim is not writeable or does not exist + * @param array $data mail-settings + * @return boolean - true when the amavis-config and the dkim-dir are writeable */ function check_system($data) { global $app,$mail_config; @@ -105,25 +111,29 @@ class mail_plugin_dkim { $check=false; } } else { - $app->log('Unable to write DKIM settings. Check your config!',LOGLEVEL_ERROR); + $app->log('Unable to write DKIM settings; Check your config!',LOGLEVEL_ERROR); $check=false; } return $check; } - /* - This function restarts amavis + /** + * This function restarts amavis */ function restart_amavis() { global $app,$conf; $initfile=$conf['init_scripts'].'/amavis'; - $app->log('Reloading amavis.',LOGLEVEL_DEBUG); - exec(escapeshellarg($conf['init_scripts']).escapeshellarg('/amavis').' reload',$output); + $app->log('Restarting amavis.',LOGLEVEL_DEBUG); + exec(escapeshellarg($conf['init_scripts']).escapeshellarg('/amavis').' restart',$output); foreach($output as $logline) $app->log($logline,LOGLEVEL_DEBUG); } - /* - This function writes the keyfiles (public and private) + /** + * This function writes the keyfiles (public and private) + * @param string $key_file full path to the key-file + * @param string $key_value private-key + * @param string $key_domain mail-domain + * @return bool - true when the key is written to disk */ function write_dkim_key($key_file,$key_value,$key_domain) { global $app,$mailconfig; @@ -143,8 +153,10 @@ class mail_plugin_dkim { return $success; } - /* - This function removes the keyfiles + /** + * This function removes the keyfiles + * @param string $key_file full path to the key-file + * @param string $key_domain mail-domain */ function remove_dkim_key($key_file,$key_domain) { global $app; @@ -158,8 +170,9 @@ class mail_plugin_dkim { } else $app->log('Unable to delete the DKIM Public-key for '.$key_domain.' (not found).',LOGLEVEL_DEBUG); } - /* - This function adds the entry to the amavisd-config + /** + * This function adds the entry to the amavisd-config + * @param string $key_domain mail-domain */ function add_to_amavis($key_domain) { global $app,$mail_config; @@ -172,8 +185,9 @@ class mail_plugin_dkim { } } - /* - This function removes the entry from the amavisd-config + /** + * This function removes the entry from the amavisd-config + * @param string $key_domain mail-domain */ function remove_from_amavis($key_domain) { global $app; @@ -193,24 +207,31 @@ class mail_plugin_dkim { } else $app->log('Unable to delete the DKIM settings from amavis-config for '.$key_domain.'.',LOGLEVEL_ERROR); } - /* - This function controlls new key-files and amavisd-entries + /** + * This function controlls new key-files and amavisd-entries + * @param array $data mail-settings */ function add_dkim($data) { global $app; - $mail_config = $app->getconf->get_server_config($conf['server_id'], 'mail'); - if ( substr($mail_config['dkim_path'],strlen($mail_config['dkim_path'])-1) == '/' ) - $mail_config['dkim_path'] = substr($mail_config['dkim_path'],0,strlen($mail_config['dkim_path'])-1); - if ($this->write_dkim_key($mail_config['dkim_path']."/".$data['new']['domain'],$data['new']['dkim_private'],$data['new']['domain'])) { - $this->add_to_amavis($data['new']['domain']); - } else { - $app->log('Error saving the DKIM Private-key for '.$data['new']['domain'].' - DKIM is not enabled for the domain.',LOGLEVEL_ERROR); + if ($data['new']['active'] == 'y') { + $mail_config = $app->getconf->get_server_config($conf['server_id'], 'mail'); + if ( substr($mail_config['dkim_path'],strlen($mail_config['dkim_path'])-1) == '/' ) + $mail_config['dkim_path'] = substr($mail_config['dkim_path'],0,strlen($mail_config['dkim_path'])-1); + if ($this->write_dkim_key($mail_config['dkim_path']."/".$data['new']['domain'],$data['new']['dkim_private'],$data['new']['domain'])) { + $this->add_to_amavis($data['new']['domain']); + } else { + $app->log('Error saving the DKIM Private-key for '.$data['new']['domain'].' - DKIM is not enabled for the domain.',LOGLEVEL_ERROR); + } + } + else { + $app->log('DKIM for '.$data['new']['domain'].' not written to disk - domain is inactive',LOGLEVEL_DEBUG); } } - /* - This function controlls the removement of keyfiles (public and private) - and the entry in the amavisd-config + /** + * This function controlls the removement of keyfiles (public and private) + * and the entry in the amavisd-config + * @param array $data mail-settings */ function remove_dkim($_data) { global $app; @@ -221,19 +242,28 @@ class mail_plugin_dkim { $this->remove_from_amavis($_data['domain']); } - /* - Functions called by onLoad + /** + * Function called by onLoad + * deletes dkim-keys */ function domain_dkim_delete($event_name,$data) { if (isset($data['old']['dkim']) && $data['old']['dkim'] == 'y') $this->remove_dkim($data['old']); } + /** + * Function called by onLoad + * insert dkim-keys + */ function domain_dkim_insert($event_name,$data) { if (isset($data['new']['dkim']) && $data['new']['dkim']=='y' && $this->check_system($data)) { $this->add_dkim($data); } } + /** + * Function called by onLoad + * chang dkim-settings + */ function domain_dkim_update($event_name,$data) { global $app; /* get the config */ -- GitLab