From 56f69880ecab67ba23df54aa849d7f4e1d600da8 Mon Sep 17 00:00:00 2001
From: tbrehm <t.brehm@ispconfig.org>
Date: Fri, 19 Apr 2013 05:45:30 -0400
Subject: [PATCH] Add dkim support (Patch by Florian).

---
 server/plugins-available/mail_plugin_dkim.inc.php         |  286 ++++++++++++++++++++++++++++
 interface/web/mail/form/mail_domain.tform.php             |   29 ++
 install/tpl/opensuse_amavisd_conf.master                  |    6 
 interface/web/admin/lib/lang/en_server_config.lng         |    2 
 install/tpl/server.ini.master                             |    1 
 interface/web/admin/templates/server_config_mail_edit.htm |    6 
 interface/web/mail/mail_domain_dkim_create.php            |   90 +++++++++
 interface/web/admin/form/server_config.tform.php          |   13 +
 install/tpl/amavisd_user_config.master                    |    6 
 interface/web/mail/templates/mail_domain_edit.htm         |   61 ++++++
 interface/web/mail/lib/lang/en_mail_domain.lng            |    8 
 install/sql/ispconfig3.sql                                |    9 
 install/sql/incremental/upd_0052.sql                      |    3 
 interface/lib/classes/validate_dkim.inc.php               |   60 ++++++
 14 files changed, 573 insertions(+), 7 deletions(-)

diff --git a/install/sql/incremental/upd_0052.sql b/install/sql/incremental/upd_0052.sql
new file mode 100644
index 0000000..b8e3829
--- /dev/null
+++ b/install/sql/incremental/upd_0052.sql
@@ -0,0 +1,3 @@
+ALTER TABLE `mail_domain` ADD `dkim_public` MEDIUMTEXT NOT NULL AFTER `domain`;
+ALTER TABLE `mail_domain` ADD `dkim_private` MEDIUMTEXT NOT NULL AFTER `domain`;
+ALTER TABLE `mail_domain` ADD `dkim` ENUM( 'n', 'y' ) NOT NULL AFTER `domain`;
diff --git a/install/sql/ispconfig3.sql b/install/sql/ispconfig3.sql
index 6eb264a..2a2d4a0 100644
--- a/install/sql/ispconfig3.sql
+++ b/install/sql/ispconfig3.sql
@@ -663,6 +663,9 @@
   `sys_perm_other` varchar(5) NOT NULL default '',
   `server_id` int(11) unsigned NOT NULL default '0',
   `domain` varchar(255) NOT NULL default '',
+  `dkim` ENUM( 'n', 'y' ) NOT NULL default 'n',
+  `dkim_private` mediumtext NOT NULL default '',
+  `dkim_public` mediumtext NOT NULL default '',
   `active` enum('n','y') NOT NULL,
   PRIMARY KEY  (`domain_id`),
   KEY `server_id` (`server_id`,`domain`),
@@ -2169,7 +2172,7 @@
 --
 -- Dumping data for table `sys_config`
 --
-
+
 INSERT INTO sys_config VALUES ('1','db','db_version','3.0.5.2');
-
-SET FOREIGN_KEY_CHECKS = 1;
\ No newline at end of file
+
+SET FOREIGN_KEY_CHECKS = 1;
diff --git a/install/tpl/amavisd_user_config.master b/install/tpl/amavisd_user_config.master
index c89f875..f377955 100644
--- a/install/tpl/amavisd_user_config.master
+++ b/install/tpl/amavisd_user_config.master
@@ -75,6 +75,12 @@
 # Set the log_level to 5 for debugging
 $log_level = 0;                # (defaults to 0)
 
+# DKIM
+
+$enable_dkim_verification = 1;
+$enable_dkim_signing = 1; # load DKIM signing code,
+@dkim_signature_options_bysender_maps = (
+{ '.' => { ttl => 21*24*3600, c => 'relaxed/simple' } } );
 
 #------------ Do not modify anything below this line -------------
 1;  # insure a defined return
diff --git a/install/tpl/opensuse_amavisd_conf.master b/install/tpl/opensuse_amavisd_conf.master
index d0d8a87..5f9f867 100644
--- a/install/tpl/opensuse_amavisd_conf.master
+++ b/install/tpl/opensuse_amavisd_conf.master
@@ -778,5 +778,11 @@
 $DO_SYSLOG = 1;
 $LOGFILE = "/var/log/amavis.log";  # (defaults to empty, no log)
 
+# DKIM
+
+$enable_dkim_verification = 1;
+$enable_dkim_signing = 1; # load DKIM signing code,
+@dkim_signature_options_bysender_maps = (
+{ '.' => { ttl => 21*24*3600, c => 'relaxed/simple' } } );
 
 1;  # insure a defined return
diff --git a/install/tpl/server.ini.master b/install/tpl/server.ini.master
index 3f3dc36..007d51b 100644
--- a/install/tpl/server.ini.master
+++ b/install/tpl/server.ini.master
@@ -21,6 +21,7 @@
 module=postfix_mysql
 maildir_path=/var/vmail/[domain]/[localpart]
 homedir_path=/var/vmail
+dkim_path=/etc/postfix/dkim
 pop3_imap_daemon=courier
 mail_filter_syntax=maildrop
 mailuser_uid=5000
diff --git a/interface/lib/classes/validate_dkim.inc.php b/interface/lib/classes/validate_dkim.inc.php
new file mode 100644
index 0000000..fccd457
--- /dev/null
+++ b/interface/lib/classes/validate_dkim.inc.php
@@ -0,0 +1,60 @@
+<?php
+
+/*
+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.
+*/
+
+class validate_dkim {
+	
+	function get_error($errmsg) {
+		global $app;
+        	if(isset($app->tform->wordbook[$errmsg])) {
+			return $app->tform->wordbook[$errmsg]."<br>\r\n";
+		} else {
+			return $errmsg."<br>\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 "'.$field_value.'"|openssl rsa -check',$output,$result);
+			if($result != 0) return $this->get_error($validator['errmsg']);
+		}
+	}
+
+	/* Validator function for DKIM Path */
+	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']);
+	}
+
+}
diff --git a/interface/web/admin/form/server_config.tform.php b/interface/web/admin/form/server_config.tform.php
index 0f0c38b..fb5bf87 100644
--- a/interface/web/admin/form/server_config.tform.php
+++ b/interface/web/admin/form/server_config.tform.php
@@ -211,6 +211,19 @@
 			'width' => '40',
 			'maxlength' => '255'
 		),
+                'dkim_path' => array(
+                        'datatype' => 'VARCHAR',
+                        'formtype' => 'TEXT',
+                        'default' => '/var/db/dkim',
+                        'validators'    => array (  0 => array ('type'  => 'CUSTOM',
+                                                                'class' => 'validate_dkim',
+                                                                'function' => 'check_dkim_path',
+                                                                'errmsg'=> 'dkim_path_error'),
+                                                 ),
+                        'value' => '',
+                        'width' => '40',
+                        'maxlength' => '255'
+                ),
 		'pop3_imap_daemon' => array(
 			'datatype' => 'VARCHAR',
 			'formtype' => 'SELECT',
diff --git a/interface/web/admin/lib/lang/en_server_config.lng b/interface/web/admin/lib/lang/en_server_config.lng
index b39dd0e..b9f270e 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["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 fba0bf7..0374931 100644
--- a/interface/web/admin/templates/server_config_mail_edit.htm
+++ b/interface/web/admin/templates/server_config_mail_edit.htm
@@ -20,6 +20,10 @@
                 <input name="homedir_path" id="homedir_path" value="{tmpl_var name='homedir_path'}" size="40" maxlength="255" type="text" class="textInput" />
             </div>
             <div class="ctrlHolder">
+                <label for="dkim_path">{tmpl_var name='dkim_path_txt'}</label>
+                <input name="dkim_path" id="dkim_path" value="{tmpl_var name='dkim_path'}" size="40" maxlength="255" type="text" class="textInput" />
+            </div>
+            <div class="ctrlHolder">
                 <p class="label">{tmpl_var name='pop3_imap_daemon_txt'}</p>
                 <div class="multiField">
                     <select name="pop3_imap_daemon" id="pop3_imap_daemon" class="selectInput">
@@ -95,4 +99,4 @@
         </div>
     </div>
     
-</div>
\ No newline at end of file
+</div>
diff --git a/interface/web/mail/form/mail_domain.tform.php b/interface/web/mail/form/mail_domain.tform.php
index 506f934..caf48d5 100644
--- a/interface/web/mail/form/mail_domain.tform.php
+++ b/interface/web/mail/form/mail_domain.tform.php
@@ -98,6 +98,33 @@
 			'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 @@
 );
 
 
-?>
\ No newline at end of file
+?>
diff --git a/interface/web/mail/lib/lang/en_mail_domain.lng b/interface/web/mail/lib/lang/en_mail_domain.lng
index ad93100..00ddf33 100644
--- a/interface/web/mail/lib/lang/en_mail_domain.lng
+++ b/interface/web/mail/lib/lang/en_mail_domain.lng
@@ -3,11 +3,17 @@
 $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_generate_txt"] = 'Generate DKIM Private-key';
+$wb["dkim_dns_txt"] = 'DNS-Record (TYPE TXT)<br><br>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 0000000..758d3af
--- /dev/null
+++ b/interface/web/mail/mail_domain_dkim_create.php
@@ -0,0 +1,90 @@
+<?php
+/*
+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 script is invoked by the java-script in interface/web/mail/templates/mail_domain_edit.htm
+	when generating the DKIM Private-key.
+
+	return DKIM Private-Key and DNS-record
+*/ 
+
+require_once('../../lib/config.inc.php');
+
+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
+*/
+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;
+}
+
+function dns_record() {
+	global $private_key;
+	$public_key='';
+	exec('echo "'.$private_key.'"|openssl rsa -pubout -outform PEM',$pubkey,$result);
+	$pubkey=array_diff($pubkey,array('-----BEGIN PUBLIC KEY-----','-----END PUBLIC KEY-----'));	
+	foreach($pubkey as $values) $public_key=$public_key.$values."\n";
+	$dns_record="HOSTNAME: default._domainkey.".$_POST['domain'].".\n\nTEXT: v=DKIM1; t=s; p=".$public_key;
+	return $dns_record;
+}
+
+$_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";
+		$dns_record=dns_record();
+	break;
+	case 'show': /* show the DNS-Record onLoad */
+		$private_key=$_POST['pkey'];
+		$dns_record=dns_record();
+	break;
+}
+echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+echo "<formatname>\n";
+echo "<privatekey>".$private_key."</privatekey>\n";
+echo "<dnsrecord>".$dns_record."</dnsrecord>\n";
+echo "</formatname>\n";
+?>
diff --git a/interface/web/mail/templates/mail_domain_edit.htm b/interface/web/mail/templates/mail_domain_edit.htm
index 5a1b613..81cdb73 100644
--- a/interface/web/mail/templates/mail_domain_edit.htm
+++ b/interface/web/mail/templates/mail_domain_edit.htm
@@ -59,6 +59,24 @@
                     {tmpl_var name='active'}
                 </div>
             </div>
+            <div class="subsectiontoggle"><span></span>{tmpl_var name='dkim_settings_txt'}<em></em></div>
+            <div style="display:none;">
+              <div class="ctrlHolder">
+                <p class="label">{tmpl_var name='dkim_txt'}</p>
+                <div class="multiField">
+                  {tmpl_var name='dkim'}
+                </div>
+              </div>
+              <div class="ctrlHolder">
+                <label for="dkim_private">{tmpl_var name='dkim_private_txt'}</label>
+                <textarea name="dkim_private" id="dkim_private" rows='10' cols='30'>{tmpl_var name='dkim_private'}</textarea>
+<a href="javascript:setRequest('create','{tmpl_var name='domain'}')">{tmpl_var name='dkim_generate_txt'}</a>
+              </div>
+              <div class="ctrlHolder">
+                <label for="dkim_dns">{tmpl_var name='dkim_dns_txt'}</label>
+                <textarea name="dkim_dns" id="dkim_dns" rows='10' cols='30'>{tmpl_var name='dkim_dns'}</textarea>
+              </div>
+            </div>
         </fieldset>
 
         <input type="hidden" name="id" value="{tmpl_var name='id'}">
@@ -70,4 +88,45 @@
         </div>
     </div>
 
-</div>
\ No newline at end of file
+</div>
+<script language="JavaScript" type="text/javascript">
+        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_dns')[0].value = request.responseXML.getElementsByTagName('dnsrecord')[0].firstChild.nodeValue;
+                                }
+                                break;
+                        default:
+                                break;
+                }
+        }
+
+var serverType = jQuery('#dkim_private').val();
+setRequest('show','{tmpl_var name="domain"}',serverType);
+</script>
+
diff --git a/server/plugins-available/mail_plugin_dkim.inc.php b/server/plugins-available/mail_plugin_dkim.inc.php
new file mode 100644
index 0000000..8b098cc
--- /dev/null
+++ b/server/plugins-available/mail_plugin_dkim.inc.php
@@ -0,0 +1,286 @@
+<?php
+
+/*
+todo:
+- DNS interaction
+*/
+/*
+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.
+*/
+
+class mail_plugin_dkim {
+
+	var $plugin_name = 'mail_plugin_dkim';
+	var $class_name = 'mail_plugin_dkim';
+
+	// private variables
+	var $action = '';
+
+	/* 
+		This function is called during ispconfig installation to determine
+		if a symlink shall be created for this plugin.
+	*/
+	function onInstall() {
+		global $conf;
+
+		if($conf['services']['mail'] == true) {
+			return true;
+		} else {
+			return false;
+		}
+
+	}
+
+	/*
+	 	This function is called when the plugin is loaded
+	*/
+	function onLoad() {
+		global $app,$conf;
+		/*
+		Register for the events
+		*/
+		$app->plugins->registerEvent('mail_domain_delete',$this->plugin_name,'domain_dkim_delete');
+		$app->plugins->registerEvent('mail_domain_insert',$this->plugin_name,'domain_dkim_insert');
+		$app->plugins->registerEvent('mail_domain_update',$this->plugin_name,'domain_dkim_update');
+
+                // Register service
+                $app->services->registerService('amavisd','mail_module','restartAmavisd');
+	}
+
+        /*
+                This function gets the amavisd-config file
+        */
+	function get_amavis_config() {
+		$pos_config=array(
+			'/etc/amavisd.conf',
+			'/etc/amavisd.conf/50-user'
+		);
+		$amavis_configfile='';
+                foreach($pos_config as $conf) {
+			if (is_file($conf)) {
+				$amavis_configfile=$conf;
+				break;
+			}
+                }
+		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
+	*/
+	function check_system($data) {
+		global $app,$mail_config;
+                $app->uses('getconf');
+		$check=true;
+		/* check for amavis-config */
+		if ( $this->get_amavis_config() == '' || !is_writeable($this->get_amavis_config()) ) {
+			$app->log('Amavis-config not found or not writeable.',LOGLEVEL_ERROR);
+			$check=false;
+		}
+		/* dir for dkim-keys writeable? */
+                $mail_config = $app->getconf->get_server_config($conf['server_id'], 'mail');
+		if (isset($mail_config['dkim_path']) && isset($data['new']['dkim_private']) && !empty($data['new']['dkim_private'])) {
+			if (!is_writeable($mail_config['dkim_path'])) {
+				$app->log('DKIM Path '.$mail_config['dkim_path'].' not found or not writeable.',LOGLEVEL_ERROR);
+				$check=false;
+               		}
+		} else {
+			$app->log('Unable to write DKIM settings; Check your config!',LOGLEVEL_ERROR);
+			$check=false;
+		}
+		if (!$check) {
+               		$app->db->query("UPDATE mail_domain SET dkim = 'n' WHERE domain = '".$data['new']['domain']."'");
+               		$app->dbmaster->query("UPDATE mail_domain SET dkim = 'n' WHERE domain = '".$data['new']['domain']."'");
+		}
+		return $check;
+	}
+        
+	/*
+		This function restarts amavis
+	*/
+	function restart_amavis() {
+		global $app,$conf;
+		$initfile=$conf['init_scripts'].'/amavis';
+		$app->log('Restarting amavis.',LOGLEVEL_DEBUG);
+		exec($conf['init_scripts'].'/amavis restart',$output);
+		foreach($output as $logline) $app->log($logline,LOGLEVEL_DEBUG);
+	}
+
+	/*
+                This function writes the keyfiles (public and private)
+                The public-key is always created and stored into the db and local key-file
+        */
+	function write_dkim_key($key_file,$key_value,$key_domain) {
+                global $app,$mailconfig;
+		$success=false;
+		if (!file_put_contents($key_file.'.private',$key_value) === false) { 
+			$app->log('Saved DKIM Private-key to '.$key_file.'.private',LOGLEVEL_DEBUG);
+			$success=true;
+			/* now we get the DKIM Public-key */
+			exec('cat "'.$key_file.'.private'.'"|openssl rsa -pubout',$pubkey,$result);
+			$public_key='';
+			foreach($pubkey as $values) $public_key=$public_key.$values."\n";
+			/* save the DKIM Public-key in dkim-dir */
+			if (!file_put_contents($key_file.'.public',$public_key) === false) 
+				$app->log('Saved DKIM Public to '.$key_domain.'.',LOGLEVEL_DEBUG);
+			else $app->log('Unable to save DKIM Public to '.$key_domain.'.',LOGLEVEL_WARNING);
+			/* store the private-key to the databse(s) */
+			$app->log('Store the DKIM Public-key in database.',LOGLEVEL_DEBUG);
+       	     		$app->db->query("UPDATE mail_domain SET dkim_public = '".$public_key."' WHERE domain = '".$ky_domain."'");
+   	     		$app->dbmaster->query("UPDATE mail_domain SET dkim_public = '".$public_key."' WHERE domain = '".$key_domain."'");
+		} 
+		return $success;
+	}
+
+	/*
+		This function removes the keyfiles
+	*/
+	function remove_dkim_key($key_file,$key_domain) {
+		global $app;
+		if (file_exists($key_file.'.private')) {
+			exec('rm -f '.$key_file.'.private');
+			$app->log('Deleted the DKIM Private-key for '.$key_domain.'.',LOGLEVEL_DEBUG);
+		} else $app->log('Unable to delete the DKIM Private-key for '.$key_domain.' (not found).',LOGLEVEL_DEBUG);
+		if (file_exists($key_file.'.public')) {
+			exec('rm -f '.$key_file.'.public');
+			$app->log('Deleted the DKIM Public-key for '.$key_domain.'.',LOGLEVEL_DEBUG);
+		} 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
+	*/
+	function add_to_amavis($key_domain) {
+		global $app,$mail_config;
+		$amavis_config = file_get_contents($this->get_amavis_config());
+	       	$key_value="dkim_key('".$key_domain."', 'default', '".$mail_config['dkim_path']."/".$key_domain.".private');\n";
+       	        if(strpos($amavis_config, $key_value) !== false) $amavis_config = str_replace($key_value, '', $amavis_config);
+		if (!file_put_contents($this->get_amavis_config(),$key_value,FILE_APPEND) === false) {
+			$app->log('Adding DKIM Private-key to amavis-config.',LOGLEVEL_DEBUG);
+			$this->restart_amavis();
+		}
+	}
+
+	/*
+		This function removes the entry from the amavisd-config
+	*/
+	function remove_from_amavis($key_domain) {
+		global $app;
+		$amavis_config = file($this->get_amavis_config());
+		$i=0;$found=false;
+		foreach($amavis_config as $line) {
+			if (preg_match("/^\bdkim_key\b.*\b".$key_domain."\b/",$line)) {
+				unset($amavis_config[$i]);
+				$found=true;
+			}
+			$i++;
+		}
+		if ($found) {
+			file_put_contents($this->get_amavis_config(), $amavis_config);
+			$app->log('Deleted the DKIM settings from amavis-config for '.$key_domain.'.',LOGLEVEL_DEBUG);
+			$this->restart_amavis();
+		} 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
+	*/
+	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 now disabled for the domain.',LOGLEVEL_ERROR);
+               		$app->db->query("UPDATE mail_domain SET dkim = 'n' WHERE domain = '".$data['new']['domain']."'");
+               		$app->dbmaster->query("UPDATE mail_domain SET dkim = 'n' WHERE domain = '".$data['new']['domain']."'");
+		}
+	}
+
+	/*
+		This function controlls the removement of keyfiles (public and private)
+		and the entry in the amavisd-config
+	*/
+	function remove_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);
+		$this->remove_dkim_key($mail_config['dkim_path']."/".$_data['domain'],$_data['domain']);
+               	$this->remove_from_amavis($_data['domain']);
+	}
+
+	/*
+		Functions called by onLoad
+	*/
+	function domain_dkim_delete($event_name,$data) {
+		if (isset($data['old']['dkim']) && $data['old']['dkim'] == 'y') $this->remove_dkim($data['old']);
+	}
+
+	function domain_dkim_insert($event_name,$data) {
+		if (isset($data['new']['dkim']) && $data['new']['dkim']=='y' && $this->check_system($data)) {
+			/* if the domain is already defined, remove from amavis */
+			$this->remove_from_amavis($data['new']['domain']);
+//			$this->remove_from_amavis("dkim_key('".$data['new']['domain']."', 'default', '".$mail_config['dkim_path']."/".$data['new']['domain'].".private');\n",$data['new']['domain']);
+			$this->add_dkim($data);
+		}
+	}
+
+	function domain_dkim_update($event_name,$data) {
+		global $app;
+                /* get the config */
+		if (isset($data['new']['dkim']) && $data['new']['dkim']=='y') { /* DKIM enabled */
+			if ($this->check_system($data)) {
+				/* new domain-name */
+        		        if ($data['old']['domain'] != $data['new']['domain']) {
+					$this->remove_dkim($data['old']);
+					$this->add_dkim($data);
+				}
+				/* new key */
+        		        if (($data['old']['dkim_private'] != $data['new']['dkim_private']) || ($data['old']['dkim'] != $data['new']['dkim'])) {
+				if ($data['new']['dkim_private'] != $data['old']['dkim_private']) $this->remove_dkim($data['new']);
+					$this->add_dkim($data);
+				}
+				/* change active (on / off) */
+                		if ($data['old']['active'] != $data['new']['active']) {
+                        		if ($data['new']['active'] == 'y') {
+	                        		$this->add_dkim($data);
+					} else {
+        	                		$this->remove_dkim($data['new']);
+	        	                }
+        	        	}
+			}	
+		}
+		if (isset($data['new']['dkim']) && $data['old']['dkim'] != $data['new']['dkim'])
+			if ($this->check_system($data) && $data['new']['dkim'] == 'n') $this->remove_dkim($data['new']);
+	}
+}
+?>

--
Gitblit v1.9.1