Enigma: Code refactoring with better handling of encrypted-inside-encrypted (e.g. forwarded) and signed+ncrypted messages

pull/322/head
Aleksander Machniak 9 years ago
parent c9e2ab488e
commit 53fa08d8ae

@ -302,17 +302,18 @@ class enigma_engine
* Handler for message_part_structure hook. * Handler for message_part_structure hook.
* Called for every part of the message. * Called for every part of the message.
* *
* @param array Original parameters * @param array Original parameters
* @param string Part body (will be set if used internally)
* *
* @return array Modified parameters * @return array Modified parameters
*/ */
function part_structure($p) function part_structure($p, $body = null)
{ {
if ($p['mimetype'] == 'text/plain' || $p['mimetype'] == 'application/pgp') { if ($p['mimetype'] == 'text/plain' || $p['mimetype'] == 'application/pgp') {
$this->parse_plain($p); $this->parse_plain($p, $body);
} }
else if ($p['mimetype'] == 'multipart/signed') { else if ($p['mimetype'] == 'multipart/signed') {
$this->parse_signed($p); $this->parse_signed($p, $body);
} }
else if ($p['mimetype'] == 'multipart/encrypted') { else if ($p['mimetype'] == 'multipart/encrypted') {
$this->parse_encrypted($p); $this->parse_encrypted($p);
@ -355,9 +356,10 @@ class enigma_engine
/** /**
* Handler for plain/text message. * Handler for plain/text message.
* *
* @param array Reference to hook's parameters * @param array Reference to hook's parameters
* @param string Part body (will be set if used internally)
*/ */
function parse_plain(&$p) function parse_plain(&$p, $body = null)
{ {
$part = $p['structure']; $part = $p['structure'];
@ -367,7 +369,9 @@ class enigma_engine
} }
// Get message body from IMAP server // Get message body from IMAP server
$body = $this->get_part_body($p['object'], $part); if ($body === null) {
$body = $this->get_part_body($p['object'], $part);
}
// @TODO: big message body could be a file resource // @TODO: big message body could be a file resource
// PGP signed message // PGP signed message
@ -383,15 +387,16 @@ class enigma_engine
/** /**
* Handler for multipart/signed message. * Handler for multipart/signed message.
* *
* @param array Reference to hook's parameters * @param array Reference to hook's parameters
* @param string Part body (will be set if used internally)
*/ */
function parse_signed(&$p) function parse_signed(&$p, $body = null)
{ {
$struct = $p['structure']; $struct = $p['structure'];
// S/MIME // S/MIME
if ($struct->parts[1] && $struct->parts[1]->mimetype == 'application/pkcs7-signature') { if ($struct->parts[1] && $struct->parts[1]->mimetype == 'application/pkcs7-signature') {
$this->parse_smime_signed($p); $this->parse_smime_signed($p, $body);
} }
// PGP/MIME: RFC3156 // PGP/MIME: RFC3156
// The multipart/signed body MUST consist of exactly two parts. // The multipart/signed body MUST consist of exactly two parts.
@ -403,7 +408,7 @@ class enigma_engine
&& count($struct->parts) == 2 && count($struct->parts) == 2
&& $struct->parts[1] && $struct->parts[1]->mimetype == 'application/pgp-signature' && $struct->parts[1] && $struct->parts[1]->mimetype == 'application/pgp-signature'
) { ) {
$this->parse_pgp_signed($p); $this->parse_pgp_signed($p, $body);
} }
} }
@ -499,9 +504,10 @@ class enigma_engine
* Handler for PGP/MIME signed message. * Handler for PGP/MIME signed message.
* Verifies signature. * Verifies signature.
* *
* @param array Reference to hook's parameters * @param array Reference to hook's parameters
* @param string Part body (will be set if used internally)
*/ */
private function parse_pgp_signed(&$p) private function parse_pgp_signed(&$p, $body = null)
{ {
if (!$this->rc->config->get('enigma_signatures', true)) { if (!$this->rc->config->get('enigma_signatures', true)) {
return; return;
@ -520,8 +526,14 @@ class enigma_engine
// Get bodies // Get bodies
// Note: The first part body need to be full part body with headers // Note: The first part body need to be full part body with headers
// it also cannot be decoded // it also cannot be decoded
$msg_body = $this->get_part_body($p['object'], $msg_part, true); if ($body !== null) {
$sig_body = $this->get_part_body($p['object'], $sig_part); // set signed part body
list($msg_body, $sig_body) = $this->explode_signed_body($body, $struct->ctype_parameters['boundary']);
}
else {
$msg_body = $this->get_part_body($p['object'], $msg_part, true);
$sig_body = $this->get_part_body($p['object'], $sig_part);
}
// Verify // Verify
$sig = $this->pgp_verify($msg_body, $sig_body); $sig = $this->pgp_verify($msg_body, $sig_body);
@ -543,38 +555,16 @@ class enigma_engine
* Handler for S/MIME signed message. * Handler for S/MIME signed message.
* Verifies signature. * Verifies signature.
* *
* @param array Reference to hook's parameters * @param array Reference to hook's parameters
* @param string Part body (will be set if used internally)
*/ */
private function parse_smime_signed(&$p) private function parse_smime_signed(&$p, $body = null)
{ {
return; // @TODO
if (!$this->rc->config->get('enigma_signatures', true)) { if (!$this->rc->config->get('enigma_signatures', true)) {
return; return;
} }
// Verify signature // @TODO
if ($this->rc->action == 'show' || $this->rc->action == 'preview' || $this->rc->action == 'print') {
$this->load_smime_driver();
$struct = $p['structure'];
$msg_part = $struct->parts[0];
// Verify
$sig = $this->smime_driver->verify($struct, $p['object']);
// Store signature data for display
$this->signatures[$struct->mime_id] = $sig;
// Message can be multipart (assign signature to each subpart)
if (!empty($msg_part->parts)) {
foreach ($msg_part->parts as $part)
$this->signed_parts[$part->mime_id] = $struct->mime_id;
}
else {
$this->signed_parts[$msg_part->mime_id] = $struct->mime_id;
}
}
} }
/** /**
@ -682,24 +672,16 @@ class enigma_engine
// Parse decrypted message // Parse decrypted message
$struct = $this->parse_body($body); $struct = $this->parse_body($body);
// If there's signed content verify the signature
if ($struct->mimetype == 'multipart/signed') {
// set signed part body
$body = $this->extract_signed_body($body, $struct->ctype_parameters['boundary']);
$struct->parts[0]->enigma_body = $body;
$struct->parts[1]->enigma_body = $struct->parts[1]->body;
$this->part_structure(array(
'object' => $p['object'],
'structure' => $struct,
'mimetype' => $struct->mimetype
));
}
// Modify original message structure // Modify original message structure
$this->modify_structure($p, $struct); $this->modify_structure($p, $struct);
// Parse the structure (there may be encrypted/signed parts inside
$this->part_structure(array(
'object' => $p['object'],
'structure' => $struct,
'mimetype' => $struct->mimetype
), $body);
// Attach the decryption message to all parts // Attach the decryption message to all parts
$this->decryptions[$struct->mime_id] = $result; $this->decryptions[$struct->mime_id] = $result;
foreach ((array) $struct->parts as $sp) { foreach ((array) $struct->parts as $sp) {
@ -730,7 +712,7 @@ class enigma_engine
return; return;
} }
// $this->load_smime_driver(); // @TODO
} }
/** /**
@ -1125,13 +1107,7 @@ class enigma_engine
{ {
// @TODO: Handle big bodies using file handles // @TODO: Handle big bodies using file handles
// $enigma_body is set if this is a part already extracted if ($full) {
// from encrypted message
if ($part->enigma_body) {
$body = $part->enigma_body;
unset($part->enigma_body);
}
else if ($full) {
$storage = $this->rc->get_storage(); $storage = $this->rc->get_storage();
$body = $storage->get_raw_headers($msg->uid, $part->mime_id); $body = $storage->get_raw_headers($msg->uid, $part->mime_id);
$body .= $storage->get_raw_body($msg->uid, null, $part->mime_id); $body .= $storage->get_raw_body($msg->uid, null, $part->mime_id);
@ -1171,6 +1147,7 @@ class enigma_engine
{ {
// modify mime_parts property of the message object // modify mime_parts property of the message object
$old_id = $p['structure']->mime_id; $old_id = $p['structure']->mime_id;
foreach (array_keys($p['object']->mime_parts) as $idx) { foreach (array_keys($p['object']->mime_parts) as $idx) {
if (!$old_id || $idx == $old_id || strpos($idx, $old_id . '.') === 0) { if (!$old_id || $idx == $old_id || strpos($idx, $old_id . '.') === 0) {
unset($p['object']->mime_parts[$idx]); unset($p['object']->mime_parts[$idx]);
@ -1214,17 +1191,30 @@ class enigma_engine
} }
/** /**
* Extracts body of the multipart/signed part * Extracts body and signature of multipart/signed message body
*/ */
private function extract_signed_body($body, $boundary) private function explode_signed_body($body, $boundary)
{ {
if (!$body) {
return array();
}
$boundary = '--' . $boundary; $boundary = '--' . $boundary;
$boundary_len = strlen($boundary) + 2; $boundary_len = strlen($boundary) + 2;
$start = strpos($body, $boundary) + $boundary_len;
$end = strpos($body, $boundary, $start);
$body = substr($body, $start, $end - $start - 2);
return $body; // Find boundaries
$start = strpos($body, $boundary) + $boundary_len;
$end = strpos($body, $boundary, $start);
// Get signed body and signature
$sig = substr($body, $end + $boundary_len);
$body = substr($body, $start, $end - $start - 2);
// Cleanup signature
$sig = substr($sig, strpos($sig, "\r\n\r\n") + 4);
$sig = substr($sig, 0, strpos($sig, $boundary));
return array($body, $sig);
} }
/** /**

@ -67,18 +67,18 @@ class rcube_mime_decode
/** /**
* Performs the decoding process. * Performs the decoding process.
* *
* @param string $input The input to decode * @param string $input The input to decode
* @param bool $convert Convert result to rcube_message_part structure
* *
* @return object|bool Decoded results or False on failure * @return object|bool Decoded results or False on failure
*/ */
public function decode($input) public function decode($input, $convert = true)
{ {
list($header, $body) = $this->splitBodyHeader($input); list($header, $body) = $this->splitBodyHeader($input);
// @TODO: Since this is a part of Roundcube Framework $struct = $this->do_decode($header, $body);
// we should return rcube_message_part structure
if ($struct = $this->do_decode($header, $body)) { if ($struct && $convert) {
$struct = $this->structure_part($struct); $struct = $this->structure_part($struct);
} }
@ -194,7 +194,7 @@ class rcube_mime_decode
case 'message/rfc822': case 'message/rfc822':
$obj = new rcube_mime_decode($this->params); $obj = new rcube_mime_decode($this->params);
$return->parts[] = $obj->decode($body); $return->parts[] = $obj->decode($body, false);
unset($obj); unset($obj);
break; break;

Loading…
Cancel
Save