DAViCal
iSchedule.php
1<?php
17require_once("XMLDocument.php");
18
26{
27 public $parsed;
28 public $selector;
29 public $domain;
30 private $dk;
31 private $DKSig;
32 private $try_anyway = false;
33 private $failed = false;
34 private $failOnError = true;
35 private $subdomainsOK = true;
36 private $remote_public_key ;
37 private $required_headers = Array ( 'host', // draft 01 section 7.1 required headers
38 'originator',
39 'recipient',
40 'content-type' );
41 private $disallowed_headers = Array ( 'connection', // draft 01 section 7.1 disallowed headers
42 'keep-alive',
43 'dkim-signature',
44 'proxy-authenticate',
45 'proxy-authorization',
46 'te',
47 'trailers',
48 'transfer-encoding',
49 'upgrade' );
50
51 function __construct ( )
52 {
53 global $c;
54 $this->selector = 'cal';
55 if ( is_object ( $c ) && isset ( $c->scheduling_dkim_selector ) )
56 {
57 $this->scheduling_dkim_domain = $c->scheduling_dkim_domain ;
58 $this->scheduling_dkim_selector = $c->scheduling_dkim_selector ;
59 $this->schedule_private_key = $c->schedule_private_key ;
60 if ( ! preg_match ( '/BEGIN RSA PRIVATE KEY/', $this->schedule_private_key ) )
61 {
62 $key = file_get_contents ( $this->schedule_private_key );
63 if ( $key !== false )
64 $this->schedule_private_key = $key;
65 }
66 if ( isset ( $c->scheduling_dkim_algo ) )
67 $this->scheduling_dkim_algo = $c->scheduling_dkim_algo;
68 else
69 $this->scheduling_dkim_algo = 'sha256';
70 if ( isset ( $c->scheduling_dkim_valid_time ) )
71 $this->valid_time = $c->scheduling_dkim_valid_time;
72 }
73 }
74
78 function getTxt ()
79 {
80 global $icfg;
81 // TODO handle parents of subdomains and procuration records
82 if ( $icfg [ $this->remote_selector . '._domainkey.' . $this->remote_server ] )
83 {
84 $this->dk = $icfg [ $this->remote_selector . '._domainkey.' . $this->remote_server ];
85 return true;
86 }
87
88 $dkim = dns_get_record ( $this->remote_selector . '._domainkey.' . $this->remote_server , DNS_TXT );
89 if ( count ( $dkim ) > 0 )
90 {
91 $this->dk = $dkim [ 0 ] [ 'txt' ];
92 if ( $dkim [ 0 ] [ 'entries' ] )
93 {
94 $this->dk = '';
95 foreach ( $dkim [ 0 ] [ 'entries' ] as $v )
96 {
97 $this->dk .= trim ( $v );
98 }
99 }
100 dbg_error_log( 'ischedule', 'getTxt '. $this->dk . ' XX');
101 }
102 else
103 {
104 dbg_error_log( 'ischedule', 'getTxt FAILED '. print_r ( $dkim ) );
105 $this->failed = true;
106 return false;
107 }
108 return true;
109 }
110
114 function setTxt ( $dk )
115 {
116 $this->dk = $dk;
117 }
118
122 function parseTxt ( )
123 {
124 if ( $this->failed == true )
125 return false;
126 $clean = preg_replace ( '/\s?([;=])\s?/', '$1', $this->dk );
127 $pairs = preg_split ( '/;/', $clean );
128 $this->parsed = array();
129 foreach ( $pairs as $v )
130 {
131 list($key,$value) = preg_split ( '/=/', $v, 2 );
132 $value = trim ( $value, '\\' );
133 if ( preg_match ( '/(g|k|n|p|s|t|v)/', $key ) )
134 $this->parsed [ $key ] = $value;
135 else
136 $this->parsed_ignored [ $key ] = $value;
137 }
138 return true;
139 }
140
144 function validateKey ( )
145 {
146 $this->failed = true;
147 if ( isset ( $this->parsed [ 's' ] ) )
148 {
149 if ( ! preg_match ( '/(\*|calendar)/', $this->parsed [ 's' ] ) ) {
150 dbg_error_log( 'ischedule', 'validateKey ERROR: bad selector' );
151 return false; // not a wildcard or calendar key
152 }
153 }
154 if ( isset ( $this->parsed [ 'k' ] ) && $this->parsed [ 'k' ] != 'rsa' ) {
155 dbg_error_log( 'ischedule', 'validateKey ERROR: bad key algorythm, algo was:' . $this->parsed [ 'k' ] );
156 return false; // we only speak rsa for now
157 }
158 if ( isset ( $this->parsed [ 't' ] ) && ! preg_match ( '/^[y:s]+$/', $this->parsed [ 't' ] ) ) {
159 dbg_error_log( 'ischedule', 'validateKey ERROR: type mismatch' );
160 return false;
161 }
162 else
163 {
164 if ( preg_match ( '/y/', $this->parsed [ 't' ] ) )
165 $this->failOnError = false;
166 if ( preg_match ( '/s/', $this->parsed [ 't' ] ) )
167 $this->subdomainsOK = false;
168 }
169 if ( isset ( $this->parsed [ 'g' ] ) )
170 $this->remote_user_rule = $this->parsed [ 'g' ];
171 else
172 $this->remote_user_rule = '*';
173 if ( isset ( $this->parsed [ 'p' ] ) )
174 {
175 if ( preg_match ( '/[^A-Za-z0-9_=+\/]/', $this->parsed [ 'p' ] ) )
176 return false;
177 $data = "-----BEGIN PUBLIC KEY-----\n" . implode ("\n",str_split ( $this->parsed [ 'p' ], 64 )) . "\n-----END PUBLIC KEY-----";
178 if ( $data === false )
179 return false;
180 $this->remote_public_key = $data;
181 }
182 else {
183 dbg_error_log( 'ischedule', 'validateKey ERROR: no key in dns record' . $this->parsed [ 'p' ] );
184 return false;
185 }
186 $this->failed = false;
187 return true;
188 }
189
193 function getServer ( )
194 {
195 global $icfg;
196 if ( isset($icfg) && $icfg [ $this->domain ] )
197 {
198 $this->remote_server = $icfg [ $this->domain ] [ 'server' ];
199 $this->remote_port = $icfg [ $this->domain ] [ 'port' ];
200 $this->remote_ssl = $icfg [ $this->domain ] [ 'ssl' ];
201 return true;
202 }
203 $this->remote_ssl = false;
204 $parts = explode ( '.', $this->domain );
205 $tld = $parts [ count ( $parts ) - 1 ];
206 $len = 2;
207 if ( strlen ( $tld ) == 2 && in_array ( $tld, Array ( 'uk', 'nz' ) ) )
208 $len = 3; // some country code tlds should have 3 components
209 if ( $this->domain == 'mycaldav' || $this->domain == 'altcaldav' )
210 $len = 1;
211 while ( count ( $parts ) >= $len )
212 {
213 $r = dns_get_record ( '_ischedules._tcp.' . implode ( '.', $parts ) , DNS_SRV );
214 if ($r == false)
215 $r = Array();
216
217 {
218 $remote_server = $r [ 0 ] [ 'target' ];
219 $remote_port = $r [ 0 ] [ 'port' ];
220 $this->remote_ssl = true;
221 break;
222 }
223 if ( ! isset ( $remote_server ) )
224 {
225 $r = dns_get_record ( '_ischedule._tcp.' . implode ( '.', $parts ) , DNS_SRV );
226 if ($r == false)
227 $r = Array();
228
229 if ( 0 < count ( $r ) )
230 {
231 $remote_server = $r [ 0 ] [ 'target' ];
232 $remote_port = $r [ 0 ] [ 'port' ];
233 break;
234 }
235 }
236 array_shift ( $parts );
237 }
238 if ( ! isset ( $remote_server ) )
239 {
240 if ( $this->try_anyway == true )
241 {
242 if ( ! isset ( $remote_server ) )
243 $remote_server = $this->domain;
244 if ( ! isset ( $remote_port ) )
245 $remote_port = 80;
246 }
247 else {
248 dbg_error_log('ischedule', 'Domain %s did not have srv records for iSchedule', $this->domain );
249 return false;
250 }
251 }
252 dbg_error_log('ischedule', $this->domain . ' found srv records for ' . $remote_server . ':' . $remote_port );
253 $this->remote_server = $remote_server;
254 $this->remote_port = $remote_port;
255 return true;
256 }
257
261 function getCapabilities ( $domain = null )
262 {
263 if ( $domain != null && $this->domain != $domain )
264 $this->domain = $domain;
265 if ( ! isset ( $this->remote_server ) && isset ( $this->domain ) && ! $this->getServer ( ) )
266 return false;
267 $this->remote_url = 'http'. ( $this->remote_ssl ? 's' : '' ) . '://' .
268 $this->remote_server . ':' . $this->remote_port . '/.well-known/ischedule';
269 $remote_capabilities = file_get_contents ( $this->remote_url . '?query=capabilities' );
270 if ( $remote_capabilities === false )
271 return false;
272 $xml_parser = xml_parser_create_ns('UTF-8');
273 $this->xml_tags = array();
274 xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
275 xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING, 0 );
276 $rc = xml_parse_into_struct( $xml_parser, $remote_capabilities, $this->xml_tags );
277 if ( $rc == false ) {
278 dbg_error_log( 'ERROR', 'XML parsing error: %s at line %d, column %d',
279 xml_error_string(xml_get_error_code($xml_parser)),
280 xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) );
281 dbg_error_log('ischedule', $this->domain . ' iSchedule error parsing remote xml' );
282 return false;
283 }
284 xml_parser_free($xml_parser);
285 $xmltree = BuildXMLTree( $this->xml_tags );
286 if ( !is_object($xmltree) ) {
287 dbg_error_log('ischedule', $this->domain . ' iSchedule error in remote xml' );
288 $request->DoResponse( 406, translate("REPORT body is not valid XML data!") );
289 return false;
290 }
291 dbg_error_log('ischedule', $this->domain . ' got capabilites' );
292 $this->capabilities_xml = $xmltree;
293 return true;
294 }
295
299 function queryCapabilities ( $capability, $domain = null )
300 {
301 if ( ! isset ( $this->capabilities_xml ) )
302 {
303 dbg_error_log('ischedule', $this->domain . ' capabilities not set, quering for capability:' . $capability );
304 if ( $domain == null )
305 return false;
306 if ( $this->domain != $domain )
307 $this->domain = $domain;
308 if ( ! $this->getCapabilities ( ) )
309 return false;
310 }
311 switch ( $capability )
312 {
313 case 'VEVENT':
314 case 'VFREEBUSY':
315 case 'VTODO':
316 $comp = $this->capabilities_xml->GetPath ( 'urn:ietf:params:xml:ns:ischedule:supported-scheduling-message-set/urn:ietf:params:xml:ns:ischedule:comp' );
317 foreach ( $comp as $c )
318 {
319 if ( $c->GetAttribute ( 'name' ) == $capability )
320 return true;
321 }
322 return false;
323 case 'VFREEBUSY/REQUEST':
324 case 'VTODO/ADD':
325 case 'VTODO/REQUEST':
326 case 'VTODO/REPLY':
327 case 'VTODO/CANCEL':
328 case 'VEVENT/ADD':
329 case 'VEVENT/REQUEST':
330 case 'VEVENT/REPLY':
331 case 'VEVENT/CANCEL':
332 case 'VEVENT/PUBLISH':
333 case 'VEVENT/COUNTER':
334 case 'VEVENT/DECLINECOUNTER':
335 dbg_error_log('ischedule', $this->domain . ' xml query' );
336 $comp = $this->capabilities_xml->GetPath ( 'urn:ietf:params:xml:ns:ischedule:supported-scheduling-message-set/urn:ietf:params:xml:ns:ischedule:comp' );
337 list ( $component, $method ) = explode ( '/', $capability );
338 dbg_error_log('ischedule', $this->domain . ' quering for capability:' . count ( $comp ) . ' ' . $component );
339 foreach ( $comp as $c )
340 {
341 dbg_error_log('ischedule', $this->domain . ' quering for capability:' . $c->GetAttribute ( 'name' ) . ' == ' . $component );
342 if ( $c->GetAttribute ( 'name' ) == $component )
343 {
344 $methods = $c->GetElements ( 'urn:ietf:params:xml:ns:ischedule:method' );
345 if ( count ( $methods ) == 0 )
346 return true; // seems like we should accept everything if there are no children
347 foreach ( $methods as $m )
348 {
349 if ( $m->GetAttribute ( 'name' ) == $method )
350 return true;
351 }
352 }
353 }
354 return false;
355 default:
356 return false;
357 }
358 }
359
366 function signDKIM ( $headers, $body )
367 {
368 if ( $this->scheduling_dkim_domain == null )
369 return false;
370 $b = '';
371 if ( is_array ( $headers ) !== true )
372 return false;
373 foreach ( $headers as $key => $value )
374 {
375 $b .= $key . ': ' . $value . "\r\n";
376 }
377 $dk['v'] = '1';
378 $dk['a'] = 'rsa-' . $this->scheduling_dkim_algo;
379 $dk['s'] = $this->selector;
380 $dk['d'] = $this->scheduling_dkim_domain;
381 $dk['c'] = 'simple-http'; // implied canonicalization of simple-http/simple from rfc4871 Section-3.5
382 if ( isset ( $_SERVER['SERVER_NAME'] ) && strstr ( $_SERVER['SERVER_NAME'], $this->domain ) !== false ) // don't use when testing
383 $dk['i'] = '@' . $_SERVER['SERVER_NAME']; //optional
384 $dk['q'] = 'dns/txt'; // optional, dns/txt is the default if missing
385 $dk['l'] = strlen ( $body ); //optional
386 $dk['t'] = time ( ); // timestamp of signature, optional
387 if ( isset ( $this->valid_time ) )
388 $dk['x'] = $this->valid_time; // unix timestamp expiriation of signature, optional
389 $dk['h'] = implode ( ':', array_keys ( $headers ) );
390 $dk['bh'] = base64_encode ( hash ( 'sha256', $body , true ) );
391 $value = '';
392 foreach ( $dk as $key => $val )
393 $value .= "$key=$val; ";
394 $value .= 'b=';
395 $tosign = $b . 'DKIM-Signature: ' . $value;
396 openssl_sign ( $tosign, $sig, $this->schedule_private_key, $this->scheduling_dkim_algo );
397 $this->tosign = $tosign;
398 $value .= base64_encode ( $sig );
399 return $value;
400 }
401
408 function sendRequest ( $address, $type, $data )
409 {
410 global $session;
411 if ( empty($this->scheduling_dkim_domain) )
412 return false;
413 if ( is_array ( $address ) )
414 list ( $user, $domain ) = explode ( '@', $address[0] );
415 else
416 list ( $user, $domain ) = explode ( '@', $address );
417 if ( ! $this->getCapabilities ( $domain ) )
418 {
419 dbg_error_log('ischedule', $domain . ' did not have iSchedule capabilities for ' . $type );
420 return false;
421 }
422 dbg_error_log('ischedule', $domain . ' trying with iSchedule capabilities for ' . $type );
423 if ( $this->queryCapabilities ( $type ) )
424 {
425 dbg_error_log('ischedule', $domain . ' trying with iSchedule capabilities for ' . $type . ' OK');
426 list ( $component, $method ) = explode ( '/', $type );
427 $headers = array ( );
428 $headers['iSchedule-Version'] = '1.0';
429 $headers['Originator'] = 'mailto:' . $session->email;
430 if ( is_array ( $address ) )
431 $headers['Recipient'] = implode ( ', ' , $address );
432 else
433 $headers['Recipient'] = $address;
434 $headers['Content-Type'] = 'text/calendar; component=' . $component ;
435 if ( $method )
436 $headers['Content-Type'] .= '; method=' . $method;
437 $headers['DKIM-Signature'] = $this->signDKIM ( $headers, $data );
438 if ( $headers['DKIM-Signature'] == false )
439 return false;
440 $request_headers = array ( );
441 foreach ( $headers as $k => $v )
442 $request_headers[] = $k . ': ' . $v;
443 $curl = curl_init ( $this->remote_url );
444 curl_setopt ( $curl, CURLOPT_RETURNTRANSFER, true );
445 curl_setopt ( $curl, CURLOPT_HTTPHEADER, array() ); // start with no headers set
446 curl_setopt ( $curl, CURLOPT_HTTPHEADER, $request_headers );
447 curl_setopt ( $curl, CURLOPT_SSL_VERIFYPEER, false);
448 curl_setopt ( $curl, CURLOPT_SSL_VERIFYHOST, false);
449 curl_setopt ( $curl, CURLOPT_POST, 1);
450 curl_setopt ( $curl, CURLOPT_POSTFIELDS, $data);
451 curl_setopt ( $curl, CURLOPT_CUSTOMREQUEST, 'POST' );
452 $xmlresponse = curl_exec ( $curl );
453 $info = curl_getinfo ( $curl );
454 curl_close ( $curl );
455 if ( $info['http_code'] >= 400 )
456 {
457 dbg_error_log ( 'ischedule', 'remote server returned error (%s)', $info['http_code'] );
458 return false;
459 }
460
461 error_log ( 'remote response '. $xmlresponse . print_r ( $info, true ) );
462 $xml_parser = xml_parser_create_ns('UTF-8');
463 $xml_tags = array();
464 xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
465 xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING, 0 );
466 $rc = xml_parse_into_struct( $xml_parser, $xmlresponse, $xml_tags );
467 if ( $rc == false ) {
468 dbg_error_log( 'ERROR', 'XML parsing error: %s at line %d, column %d',
469 xml_error_string(xml_get_error_code($xml_parser)),
470 xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) );
471 return false;
472 }
473 $xmltree = BuildXMLTree( $xml_tags );
474 xml_parser_free($xml_parser);
475 if ( !is_object($xmltree) ) {
476 dbg_error_log( 'ERROR', 'iSchedule RESPONSE body is not valid XML data!' );
477 return false;
478 }
479 $resp = $xmltree->GetPath ( '/*/urn:ietf:params:xml:ns:ischedule:response' );
480 $result = array();
481 foreach ( $resp as $r )
482 {
483 $recipient = $r->GetElements ( 'urn:ietf:params:xml:ns:ischedule:recipient' );
484 $status = $r->GetElements ( 'urn:ietf:params:xml:ns:ischedule:request-status' );
485 $calendardata = $r->GetElements ( 'urn:ietf:params:xml:ns:ischedule:calendar-data' );
486 if ( count ( $recipient ) < 1 )
487 continue; // this should be an error
488 if ( count ( $calendardata ) > 0 )
489 {
490 $result [ $recipient[0]->GetContent() ] = $calendardata[0]->GetContent();
491 }
492 else
493 {
494 $result [ $recipient[0]->GetContent() ] = $status[0]->GetContent();
495 }
496 }
497 if ( count ( $result ) < 1 )
498 return false;
499 else
500 return $result;
501 }
502 else
503 return false;
504 }
505
511 function parseDKIM ( $sig )
512 {
513
514 $this->failed = true;
515 $tags = preg_split ( '/;[\s\t]/', $sig );
516 foreach ( $tags as $v )
517 {
518 list($key,$value) = preg_split ( '/=/', $v, 2 );
519 $dkim[$key] = $value;
520 }
521 // the canonicalization method is currently undefined as of draft-01 of the iSchedule spec
522 // but it does define the value, it should be simple-http. RFC4871 also defines two methods
523 // simple and relaxed, simple is probably the same as simple http
524 // relaxed allows for header case folding and whitespace folding, see section 3.4.4 of RFC4871
525 if ( ! preg_match ( '{(simple|simple-http|relaxed)(/(simple|simple-http|relaxed))?}', $dkim['c'], $matches ) ) // canonicalization method
526 return 'bad canonicalization:' . $dkim['c'] ;
527 if ( count ( $matches ) > 2 )
528 $this->body_cannon = $matches[2];
529 else
530 $this->body_cannon = $matches[1];
531 $this->header_cannon = $matches[1];
532 // signing algorythm REQUIRED
533 if ( $dkim['a'] != 'rsa-sha1' && $dkim['a'] != 'rsa-sha256' ) // we only support the minimum required
534 return 'bad signing algorythm:' . $dkim['a'] ;
535 // query method to retrieve public key, could/should we add https to the spec? REQUIRED
536 if ( $dkim['q'] != 'dns/txt' )
537 return 'bad query method';
538 // domain of the signing entity REQUIRED
539 if ( ! isset ( $dkim['d'] ) )
540 return 'missing signing domain';
541 $this->remote_server = $dkim['d'];
542 // identity of signing AGENT, OPTIONAL
543 if ( isset ( $dkim['i'] ) )
544 {
545 // if present, domain of the signing agent must be a match or a subdomain of the signing domain
546 if ( ! stristr ( $dkim['i'], $dkim['d'] ) ) // RFC4871 does not specify a case match requirement
547 return 'signing domain mismatch';
548 // grab the local part of the signing agent if it's an email address
549 if ( strstr ( $dkim [ 'i' ], '@' ) )
550 $this->remote_user = substr ( $dkim [ 'i' ], 0, strpos ( $dkim [ 'i' ], '@' ) - 1 );
551 }
552 // selector used to retrieve public key REQUIRED
553 if ( ! isset ( $dkim['s'] ) )
554 return 'missing selector';
555 $this->remote_selector = $dkim['s'];
556 // signed header fields, colon seperated REQUIRED
557 if ( ! isset ( $dkim['h'] ) )
558 return 'missing list of signed headers';
559 $this->signed_headers = preg_split ( '/:/', $dkim['h'] );
560
561 $sh = Array ();
562 foreach ( $this->signed_headers as $h )
563 {
564 $sh[] = strtolower ( $h );
565 if ( in_array ( strtolower ( $h ), $this->disallowed_headers ) )
566 return "$h is NOT allowed in signed header fields per RFC4871 or iSchedule";
567 }
568 foreach ( $this->required_headers as $h )
569 if ( ! in_array ( strtolower ( $h ), $sh ) )
570 return "$h is REQUIRED but missing in signed header fields per iSchedule";
571 // body hash REQUIRED
572 if ( ! isset ( $dkim['bh'] ) )
573 return 'missing body signature';
574 // signed header hash REQUIRED
575 if ( ! isset ( $dkim['b'] ) )
576 return 'missing signature in b field';
577 // length of body used for signing
578 if ( isset ( $dkim['l'] ) )
579 $this->signed_length = $dkim['l'];
580 $this->failed = false;
581 $this->DKSig = $dkim;
582 return true;
583 }
584
589 function parseURI ( $uri )
590 {
591 if ( preg_match ( '/^mailto:([^@]+)@([^\s\t\n]+)/', $uri, $matches ) )
592 {
593 $this->remote_user = $matches[1];
594 $this->domain = $matches[2];
595 }
596 else
597 return false;
598 }
599
604 function verifySignature ( )
605 {
606 global $request,$c;
607 $this->failed = true;
608 $signed = '';
609 foreach ( $this->signed_headers as $h )
610 if ( isset ( $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] ) )
611 $signed .= "$h: " . $_SERVER['HTTP_' . strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\r\n";
612 else
613 $signed .= "$h: " . $_SERVER[ strtoupper ( strtr ( $h, '-', '_' ) ) ] . "\r\n";
614 if ( ! isset ( $_SERVER['HTTP_ORIGINATOR'] ) || stripos ( $signed, 'Originator' ) === false ) //required header, must be signed
615 return "missing Originator";
616 if ( ! isset ( $_SERVER['HTTP_RECIPIENT'] ) || stripos ( $signed, 'Recipient' ) === false ) //required header, must be signed
617 return "missing Recipient";
618 if ( ! isset ( $_SERVER['HTTP_ISCHEDULE_VERSION'] ) || $_SERVER['HTTP_ISCHEDULE_VERSION'] != '1' ) //required header and we only speak version 1 for now
619 return "missing or mismatch ischedule-version header";
620 $body = $request->raw_post;
621 if ( ! isset ( $this->signed_length ) ) // Should we use the Content-Length header if the signed length is missing?
622 $this->signed_length = strlen ( $body );
623 else
624 $body = substr ( $body, 0, $this->signed_length );
625 if ( isset ( $this->remote_user_rule ) )
626 if ( $this->remote_user_rule != '*' && ! stristr ( $this->remote_user, $this->remote_user_rule ) )
627 return "remote user rule failure";
628 $hash_algo = preg_replace ( '/^.*(sha1|sha256).*/','$1', $this->DKSig['a'] );
629 $body_hash = base64_encode ( hash ( $hash_algo, $body , true ) );
630 if ( $this->DKSig['bh'] != $body_hash )
631 return "body hash mismatch";
632 $sig = $_SERVER['HTTP_DKIM_SIGNATURE'];
633 $sig = preg_replace ( '/ b=[^;\s\r\n\t]+/', ' b=', $sig );
634 $signed .= 'DKIM-Signature: ' . $sig;
635 $verify = openssl_verify ( $signed, base64_decode ( $this->DKSig['b'] ), $this->remote_public_key, $hash_algo );
636 if ( $verify != 1 )
637 {
638 openssl_sign ( $signed, $sigb, $this->schedule_private_key, $hash_algo );
639 $sigc = base64_encode ( $sigb );
640 $verify1 = openssl_verify ( $signed, $sigc, $this->remote_public_key, $hash_algo );
641 return "signature verification failed " . $this->remote_public_key . " \n\n". $sig . " \n" . $hash_algo . "\n". print_r ($verify,1) . " XX " . $verify1 . "\n";
642 }
643 $this->failed = false;
644 return true;
645 }
646
650 function validateRequest ( )
651 {
652 global $request;
653 if ( isset ( $_SERVER['HTTP_DKIM_SIGNATURE'] ) )
654 $sig = $_SERVER['HTTP_DKIM_SIGNATURE'];
655 else
656 {
657 $request->DoResponse( 403, translate('DKIM signature missing') );
658 return false;
659 }
660 if ( isset ( $_SERVER['HTTP_ORGANIZER'] ) )
661 $request->DoResponse( 403, translate('Organizer Missing') );
662
663 dbg_error_log ('ischedule','beginning validation');
664 $err = $this->parseDKIM ( $sig );
665 if ( $err !== true || $this->failed )
666 $request->DoResponse( 412, 'DKIM signature invalid ' . "\n" . $err . "\n" );
667 if ( ! $this->getTxt () || $this->failed ) // this could also be a 424 failed dependency response
668 $request->DoResponse( 400, translate('DKIM signature validation failed(DNS ERROR)') );
669 if ( ! $this->parseTxt () || $this->failed )
670 $request->DoResponse( 400, translate('DKIM signature validation failed(KEY Parse ERROR)') );
671 if ( ! $this->validateKey () || $this->failed )
672 $request->DoResponse( 400, translate('DKIM signature validation failed(KEY Validation ERROR)') );
673 $err = $this->verifySignature ();
674 if ( $err !== true || $this->failed )
675 $request->DoResponse( 412, translate('DKIM signature validation failed(Signature verification ERROR)') . '\n' . $err );
676 dbg_error_log ('ischedule','signature ok');
677 return true;
678 }
679}
680
parseURI( $uri)
Definition: iSchedule.php:589
getCapabilities( $domain=null)
Definition: iSchedule.php:261
validateRequest()
Definition: iSchedule.php:650
setTxt( $dk)
Definition: iSchedule.php:114
sendRequest( $address, $type, $data)
Definition: iSchedule.php:408
queryCapabilities( $capability, $domain=null)
Definition: iSchedule.php:299
parseDKIM( $sig)
Definition: iSchedule.php:511
signDKIM( $headers, $body)
Definition: iSchedule.php:366
verifySignature()
Definition: iSchedule.php:604