forked from extern/egroupware
dkim signature for posts using mailDomainSigner class, plus some code to generate and store a key-pair
This commit is contained in:
parent
49f1d43ad1
commit
33510a2f4d
@ -55,6 +55,34 @@ class ischedule_client
|
|||||||
{
|
{
|
||||||
$this->url = $url;
|
$this->url = $url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->dkim_private_key = $GLOBALS['egw_info']['server']['dkim_private_key'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate private/public key pair
|
||||||
|
*
|
||||||
|
* Private and public key are stored in api config as dkim_private_key / dkim_public_key and loaded automatic by constructor.
|
||||||
|
*
|
||||||
|
* @return string public key
|
||||||
|
*/
|
||||||
|
public static function generateKeyPair()
|
||||||
|
{
|
||||||
|
// Create the keypair
|
||||||
|
$res = openssl_pkey_new();
|
||||||
|
|
||||||
|
// Get private key
|
||||||
|
openssl_pkey_export($res, $dkim_private_key);
|
||||||
|
|
||||||
|
// Get public key
|
||||||
|
$details = openssl_pkey_get_details($res);
|
||||||
|
$dkim_public_key = $details['key'];
|
||||||
|
|
||||||
|
// store both in config
|
||||||
|
config::save_value('dkim_private_key', $dkim_private_key, 'phpgwapi');
|
||||||
|
config::save_value('dkim_public_key', $dkim_public_key, 'phpgwapi');
|
||||||
|
|
||||||
|
return $dkim_public_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EMAIL_PREG = '/^([a-z0-9][a-z0-9._-]*)?[a-z0-9]@([a-z0-9](|[a-z0-9_-]*[a-z0-9])\.)+[a-z]{2,6}$/i';
|
const EMAIL_PREG = '/^([a-z0-9][a-z0-9._-]*)?[a-z0-9]@([a-z0-9](|[a-z0-9_-]*[a-z0-9])\.)+[a-z]{2,6}$/i';
|
||||||
@ -131,11 +159,16 @@ class ischedule_client
|
|||||||
*
|
*
|
||||||
* @param string $content
|
* @param string $content
|
||||||
* @param string $content_type
|
* @param string $content_type
|
||||||
|
* @param boolean $debug=false true echo request before posting
|
||||||
* @return string
|
* @return string
|
||||||
* @throws Exception with http status code and message, if server responds other then 2xx
|
* @throws Exception with http status code and message, if server responds other then 2xx
|
||||||
*/
|
*/
|
||||||
public function post_msg($content, $content_type)
|
public function post_msg($content, $content_type, $debug=false)
|
||||||
{
|
{
|
||||||
|
if (empty($this->dkim_private_key))
|
||||||
|
{
|
||||||
|
throw new Exception('You need to generate a key pair first!');
|
||||||
|
}
|
||||||
$url_parts = parse_url($this->url);
|
$url_parts = parse_url($this->url);
|
||||||
$headers = array(
|
$headers = array(
|
||||||
'Host' => $url_parts['host'].($url_parts['port'] ? ':'.$url_parts['port'] : ''),
|
'Host' => $url_parts['host'].($url_parts['port'] ? ':'.$url_parts['port'] : ''),
|
||||||
@ -145,12 +178,13 @@ class ischedule_client
|
|||||||
'Recipient' => $this->recipient,
|
'Recipient' => $this->recipient,
|
||||||
'Content-Length' => bytes($content),
|
'Content-Length' => bytes($content),
|
||||||
);
|
);
|
||||||
$headers['DKIM-Signature'] = $this->dkim_sign($headers, $content);
|
|
||||||
$header_string = '';
|
$header_string = '';
|
||||||
foreach($headers as $name => $value)
|
foreach($headers as $name => $value)
|
||||||
{
|
{
|
||||||
$header_string .= $name.': '.$value."\r\n";
|
$header_string .= $name.': '.$value."\r\n";
|
||||||
}
|
}
|
||||||
|
$header_string .= $this->dkim_sign($headers, $content)."\r\n";
|
||||||
|
|
||||||
$opts = array('http' =>
|
$opts = array('http' =>
|
||||||
array(
|
array(
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
@ -160,6 +194,8 @@ class ischedule_client
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($debug) echo "POST $this->url HTTP/1.1\n$header_string\n$content\n";
|
||||||
|
|
||||||
// need to suppress warning, if http-status not 2xx
|
// need to suppress warning, if http-status not 2xx
|
||||||
if (($response = @file_get_contents($this->url, false, stream_context_create($opts))) === false)
|
if (($response = @file_get_contents($this->url, false, stream_context_create($opts))) === false)
|
||||||
{
|
{
|
||||||
@ -172,13 +208,29 @@ class ischedule_client
|
|||||||
/**
|
/**
|
||||||
* Calculate DKIM signature for headers and body using originators domains private key
|
* Calculate DKIM signature for headers and body using originators domains private key
|
||||||
*
|
*
|
||||||
* @param array $headers
|
* @param array $headers name => value pairs, names as in $sign_headers
|
||||||
* @param string $body
|
* @param string $body
|
||||||
* @param string $type dkim-type
|
* @param string $selector='calendar'
|
||||||
|
* @param string $sign_headers='Content-Type:Host:Originator:Recipient'
|
||||||
|
* @return string DKIM-Signature: ...
|
||||||
*/
|
*/
|
||||||
public function dkim_sign(array $headers, $body, $type='calendar')
|
public function dkim_sign(array $headers, $body, $selector='calendar', $sign_headers='Content-Type:Host:Originator:Recipient')
|
||||||
{
|
{
|
||||||
return 'dummy';
|
include_once EGW_API_INC.'/php-mail-domain-signer/lib/class.mailDomainSigner.php';
|
||||||
|
list(,$domain) = explode('@', $this->originator);
|
||||||
|
$mds = new mailDomainSigner($this->dkim_private_key, $domain, $selector);
|
||||||
|
|
||||||
|
$dkim_headers = array();
|
||||||
|
foreach(explode(':', $sign_headers) as $header)
|
||||||
|
{
|
||||||
|
$dkim_headers[] = $header.': '.$headers[$header];
|
||||||
|
}
|
||||||
|
$dkim = $mds->getDKIM(strtolower($sign_headers), $dkim_headers, $body);
|
||||||
|
|
||||||
|
// as we do http, no need to fold dkim, in fact recommendation is not to
|
||||||
|
$dkim = str_replace(array(";\r\n\t", "\r\n\t"), array('; ', ''), $dkim);
|
||||||
|
|
||||||
|
return $dkim;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,7 +21,15 @@ if (isset($_SERVER['HTTP_HOST'])) die("This is a commandline ONLY tool!\n");
|
|||||||
*/
|
*/
|
||||||
function usage($err=null)
|
function usage($err=null)
|
||||||
{
|
{
|
||||||
echo basename(__FILE__).": [--url ischedule-url] [--component (VEVENT|VFREEBUSY|VTODO) (-|ical-filename)] [--method (REQUEST(default)|RESPONSE)] recipient-email [originator-email]\n\n";
|
echo "\nUsage: ".basename(__FILE__).": [options] recipient-email [originator-email]\n\n";
|
||||||
|
echo "available options:\n\n";
|
||||||
|
echo "\t--url ischedule-url\n";
|
||||||
|
echo "\t--component (VEVENT|VFREEBUSY|VTODO) (-|ical-filename)\n";
|
||||||
|
echo "\t--method (REQUEST(default)|REPLY|CANCEL|ADD)\n";
|
||||||
|
echo "\t--generate-key-pair : generates and stores a new key pair\n";
|
||||||
|
echo "\t--public-key : outputs public key\n";
|
||||||
|
echo "\t-v|--verbose output posted message too\n";
|
||||||
|
echo "\n";
|
||||||
if ($err) echo "$err\n\n";
|
if ($err) echo "$err\n\n";
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
@ -42,10 +50,10 @@ $method = 'REQUEST';
|
|||||||
while($args[0][0] == '-')
|
while($args[0][0] == '-')
|
||||||
{
|
{
|
||||||
$option = array_shift($args);
|
$option = array_shift($args);
|
||||||
if (count($args) < 2) usage("Missing arguments for '$option'!".array2string($args));
|
|
||||||
switch($option)
|
switch($option)
|
||||||
{
|
{
|
||||||
case '--url':
|
case '--url':
|
||||||
|
if (count($args) < 2) usage("Missing arguments for '$option'!");
|
||||||
$url = array_shift($args);
|
$url = array_shift($args);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -64,6 +72,7 @@ while($args[0][0] == '-')
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case '--method':
|
case '--method':
|
||||||
|
if (count($args) < 2) usage("Missing arguments for '$option'!");
|
||||||
$method = strtoupper(array_shift($args));
|
$method = strtoupper(array_shift($args));
|
||||||
if (!in_array($method, array('REQUEST','REPLY','CANCEL','ADD')))
|
if (!in_array($method, array('REQUEST','REPLY','CANCEL','ADD')))
|
||||||
{
|
{
|
||||||
@ -71,23 +80,41 @@ while($args[0][0] == '-')
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case '-v':
|
||||||
|
case '--verbose':
|
||||||
|
$verbose = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '--generate-key-pair':
|
||||||
|
$GLOBALS['egw_info']['server']['dkim_public_key'] = ischedule_client::generateKeyPair();
|
||||||
|
echo "\nKey pair generated\n";
|
||||||
|
// fall through
|
||||||
|
case '--public-key':
|
||||||
|
if (empty($GLOBALS['egw_info']['server']['dkim_public_key'])) die("\nYou need to generate a key pair first!\n\n");
|
||||||
|
echo "\nYou need following DNS record:\n";
|
||||||
|
$public_key = preg_replace('/([-]+(BEGIN|END) PUBLIC KEY[-]+|\s*)/m', '', $GLOBALS['egw_info']['server']['dkim_public_key']);
|
||||||
|
echo "\ncalendar._domainkey IN TXT \"v=DKIM1;k=rsa;h=sha1;s=calendar;t=s;p=$public_key\"\n\n";
|
||||||
|
exit;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
usage("Unknown option '$option'!");
|
usage("Unknown option '$option'!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!count($args)) usage();
|
if (!count($args) && !($public_key || $generate_key_pair)) usage();
|
||||||
|
|
||||||
$recipient = array_shift($args);
|
$recipient = array_shift($args);
|
||||||
if ($args) $originator = array_shift($args);
|
if ($args) $originator = array_shift($args);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$client = new ischedule_client($recipient, $url);
|
$client = new ischedule_client($recipient, $url);
|
||||||
|
|
||||||
echo "\nUsing iSchedule URL: $client->url\n\n";
|
echo "\nUsing iSchedule URL: $client->url\n\n";
|
||||||
if ($originator) $client->setOriginator($originator);
|
if ($originator) $client->setOriginator($originator);
|
||||||
if ($component)
|
if ($component)
|
||||||
{
|
{
|
||||||
$content_type = 'text/calendar; component='.$component.'; method='.$method;
|
$content_type = 'text/calendar; component='.$component.'; method='.$method;
|
||||||
echo $client->post_msg($content, $content_type);
|
$response = $client->post_msg($content, $content_type, $verbose);
|
||||||
|
echo $response;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user