Aleksander Machniak
2016-01-21 c9e2ab488e047295eae76bdd0cb2d1807c191ee5
Enigma: Fix handling of encrypted + signed messages (#1490632)
4 files modified
80 ■■■■ changed files
CHANGELOG 2 ●●●●● patch | view | raw | blame | history
plugins/enigma/lib/enigma_engine.php 60 ●●●● patch | view | raw | blame | history
plugins/enigma/lib/enigma_ui.php 16 ●●●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_mime_decode.php 2 ●●●●● patch | view | raw | blame | history
CHANGELOG
@@ -1,6 +1,8 @@
CHANGELOG Roundcube Webmail
===========================
- Enigma: Fix handling of encrypted + signed messages (#1490632)
- Enigma: Fix invalid boundary use in signed messages structure
- Enable use of TLSv1.1 and TLSv1.2 for IMAP (#1490640)
- Save copy of original .htaccess file when using installto.sh script (1490623)
- Fix regression where some message attachments could be missing on edit/forward (#1490608)
plugins/enigma/lib/enigma_engine.php
@@ -367,7 +367,7 @@
        }
        // Get message body from IMAP server
        $body = $this->get_part_body($p['object'], $part->mime_id);
        $body = $this->get_part_body($p['object'], $part);
        // @TODO: big message body could be a file resource
        // PGP signed message
@@ -520,8 +520,8 @@
        // Get bodies
        // Note: The first part body need to be full part body with headers
        //       it also cannot be decoded
        $msg_body = $this->get_part_body($p['object'], $msg_part->mime_id, true);
        $sig_body = $this->get_part_body($p['object'], $sig_part->mime_id);
        $msg_body = $this->get_part_body($p['object'], $msg_part, true);
        $sig_body = $this->get_part_body($p['object'], $sig_part);
        // Verify
        $sig = $this->pgp_verify($msg_body, $sig_body);
@@ -673,7 +673,7 @@
        $part   = $struct->parts[1];
        // Get body
        $body = $this->get_part_body($p['object'], $part->mime_id);
        $body = $this->get_part_body($p['object'], $part);
        // Decrypt
        $result = $this->pgp_decrypt($body);
@@ -681,6 +681,21 @@
        if ($result === true) {
            // Parse decrypted message
            $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
            $this->modify_structure($p, $struct);
@@ -1102,20 +1117,27 @@
    /**
     * Get message part body.
     *
     * @param rcube_message Message object
     * @param string        Message part ID
     * @param bool          Return raw body with headers
     * @param rcube_message      Message object
     * @param rcube_message_part Message part
     * @param bool               Return raw body with headers
     */
    private function get_part_body($msg, $part_id, $full = false)
    private function get_part_body($msg, $part, $full = false)
    {
        // @TODO: Handle big bodies using file handles
        if ($full) {
        // $enigma_body is set if this is a part already extracted
        // from encrypted message
        if ($part->enigma_body) {
            $body = $part->enigma_body;
            unset($part->enigma_body);
        }
        else if ($full) {
            $storage = $this->rc->get_storage();
            $body    = $storage->get_raw_headers($msg->uid, $part_id);
            $body   .= $storage->get_raw_body($msg->uid, null, $part_id);
            $body    = $storage->get_raw_headers($msg->uid, $part->mime_id);
            $body   .= $storage->get_raw_body($msg->uid, null, $part->mime_id);
        }
        else {
            $body = $msg->get_part_body($part_id, false);
            $body = $msg->get_part_body($part->mime_id, false);
        }
        return $body;
@@ -1192,6 +1214,20 @@
    }
    /**
     * Extracts body of the multipart/signed part
     */
    private function extract_signed_body($body, $boundary)
    {
        $boundary     = '--' . $boundary;
        $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;
    }
    /**
     * Checks if specified message part is a PGP-key or S/MIME cert data
     *
     * @param rcube_message_part Part object
plugins/enigma/lib/enigma_ui.php
@@ -756,15 +756,21 @@
            return $p;
        }
        $engine  = $this->enigma->engine;
        $part_id = $p['part']->mime_id;
        $engine    = $this->enigma->engine;
        $part_id   = $p['part']->mime_id;
        $parent_id = preg_replace('/\.[0-9]+$/', '', $part_id);
        // Decryption status
        if (isset($engine->decryptions[$part_id])) {
        if (($status = $engine->decryptions[$part_id])
            || ($parent_id !== '' && ($status = $engine->decryptions[$parent_id]))
        ) {
            $attach_scripts = true;
            // get decryption status
            $status = $engine->decryptions[$part_id];
            // show the message only once
            unset($engine->decryptions[$part_id]);
            if ($parent_id !== '') {
                unset($engine->decryptions[$parent_id]);
            }
            // display status info
            $attrib['id'] = 'enigma-message';
program/lib/Roundcube/rcube_mime_decode.php
@@ -176,6 +176,8 @@
            case 'multipart/alternative':
            case 'multipart/related':
            case 'multipart/mixed':
            case 'multipart/signed':
            case 'multipart/encrypted':
                if (!isset($content_type['other']['boundary'])) {
                    return false;
                }