diff options
author | Christian Mollekopf <cmollekopf@gmail.com> | 2022-08-01 12:51:32 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-01 12:51:32 +0300 |
commit | e495fc73b513cae26205f0dc931abbb98f35b718 (patch) | |
tree | cba015b0c14d93371bb77abb0a87c30fe10de67a | |
parent | 47a2e2c536049fc242da483c2982bb9e875feb1e (diff) |
Enigma: WOAT Support (#8626)
* Enigma: WOAT Support
* Fixed public key extraction from dns record
Co-authored-by: Aleksander Machniak <alec@alec.pl>
Co-authored-by: Christian Mollekopf <mollekopf@apheleia-it.ch>
-rw-r--r-- | plugins/enigma/config.inc.php.dist | 5 | ||||
-rw-r--r-- | plugins/enigma/lib/enigma_engine.php | 93 | ||||
-rw-r--r-- | plugins/enigma/lib/enigma_subkey.php | 12 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_message.php | 10 |
4 files changed, 108 insertions, 12 deletions
diff --git a/plugins/enigma/config.inc.php.dist b/plugins/enigma/config.inc.php.dist index a5a5233c2..d654c764d 100644 --- a/plugins/enigma/config.inc.php.dist +++ b/plugins/enigma/config.inc.php.dist @@ -78,3 +78,8 @@ $config['enigma_passwordless'] = false; // - enigma_options_lock = ['sign'] // - dont_override = ['enigma_sign_all'] $config['enigma_options_lock'] = []; + +// Enable Kolab's Web Of Anti-Trust feature +// Fetches public keys from DNS. Default: false +// To enable set it to True or an array of domain names. +$config['enigma_woat'] = false; diff --git a/plugins/enigma/lib/enigma_engine.php b/plugins/enigma/lib/enigma_engine.php index 9fe453669..75cfde1a0 100644 --- a/plugins/enigma/lib/enigma_engine.php +++ b/plugins/enigma/lib/enigma_engine.php @@ -28,6 +28,7 @@ class enigma_engine private $pgp_driver; private $smime_driver; private $password_time; + private $sender; private $cache = []; public $decryptions = []; @@ -272,6 +273,9 @@ class enigma_engine $recipients = array_unique($recipients); + // Fetch keys from external sources, if configured + $this->sync_keys($recipients); + // find recipient public keys foreach ((array) $recipients as $email) { if ($email == $from && $sign_key) { @@ -380,6 +384,17 @@ class enigma_engine return; } + // Get the message/part sender + if (!empty($p['object']->sender) && !empty($p['object']->sender['mailto'])) { + $this->sender = $p['object']->sender['mailto']; + } + if (!empty($p['structure']->headers) && !empty($p['structure']->headers['from'])) { + $from = rcube_mime::decode_address_list($p['structure']->headers['from'], 1, false); + if (($from = current($from)) && !empty($from['mailto'])) { + $this->sender = $from['mailto']; + } + } + // Don't be tempted to support encryption in text/html parts // Because of EFAIL vulnerability we should never support this (#6289) @@ -881,6 +896,11 @@ class enigma_engine { // @TODO: Handle big bodies using (temp) files + // Import sender's key from external sources, if configured + if ($this->sender) { + $this->sync_keys([$this->sender]); + } + // Get rid of possible non-ascii characters (#5962) $sig_body = preg_replace('/[^\x00-\x7F]/', '', $sig_body); @@ -905,6 +925,11 @@ class enigma_engine { // @TODO: Handle big bodies using (temp) files + // Import sender's key from external sources, if configured + if ($this->sender) { + $this->sync_keys([$this->sender]); + } + // Get rid of possible non-ascii characters (#5962) $msg_body = preg_replace('/[^\x00-\x7F]/', '', $msg_body); @@ -1020,19 +1045,25 @@ class enigma_engine return; } - $mode = $can_sign ? enigma_key::CAN_SIGN : enigma_key::CAN_ENCRYPT; - $ret = null; + $mode = $can_sign ? enigma_key::CAN_SIGN : enigma_key::CAN_ENCRYPT; + $found = []; // check key validity and type foreach ($result as $key) { if (($subkey = $key->find_subkey($email, $mode)) && (!$can_sign || $key->get_type() == enigma_key::TYPE_KEYPAIR) ) { - $ret = $key; - break; + $found[$subkey->get_creation_date(true)] = $key; } } + // Use the most recent one + if (count($found) > 1) { + ksort($found, SORT_NUMERIC); + } + + $ret = count($found) > 0 ? array_pop($found) : null; + // cache private key info for better performance // we can skip one list_keys() call when signing and attaching a key if ($can_sign) { @@ -1442,4 +1473,58 @@ class enigma_engine ); } } + + /** + * Import public keys from DNS according to Kolab Web-Of-Anti-Trust + * + * @param array $recipients List of email addresses + */ + protected function sync_keys($recipients) + { + $import = []; + $woat = $this->rc->config->get('enigma_woat'); + + if (empty($woat)) { + return; + } + + foreach ($recipients as $recipient) { + if (!strpos($recipient, '@')) { + continue; + } + + list($local, $domain) = explode('@', $recipient); + + // Do this for configured domains only + if (is_array($woat) && !in_array_nocase($domain, $woat)) { + continue; + } + + // remove parts behind a recipient delimiter ("jeroen+Trash" => "jeroen") + $local = preg_replace('/\+.*$/', '', $local); + + $fqdn = sha1($local) . '._woat.' . $domain; + + // Fetch the TXT record(s) + if (($records = dns_get_record($fqdn, DNS_TXT)) === false) { + continue; + } + + foreach ($records as $record) { + if (strpos($record['txt'], 'v=woat1,') === 0) { + $entry = explode('public_key=', $record['txt']); + if (count($entry) == 2) { + $import[] = $entry[1]; + // For now we support only one key + break; + } + } + } + } + + // Import the fetched keys + if (!empty($import)) { + $this->import_key(implode("\n", $import)); + } + } } diff --git a/plugins/enigma/lib/enigma_subkey.php b/plugins/enigma/lib/enigma_subkey.php index ffdb3e8ce..014ab7de3 100644 --- a/plugins/enigma/lib/enigma_subkey.php +++ b/plugins/enigma/lib/enigma_subkey.php @@ -93,12 +93,18 @@ class enigma_subkey /** * Returns subkey creation date-time string * - * @return string|null + * @param bool $asInt Return the date as an integer + * + * @return string|null|int */ - function get_creation_date() + function get_creation_date($asInt = false) { if (empty($this->created)) { - return null; + return $asInt ? 0 : null; + } + + if ($asInt) { + return (int) $this->created->format('U'); } $date_format = rcube::get_instance()->config->get('date_format', 'Y-m-d'); diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index 6fd0d6f24..d95fc6c16 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -124,6 +124,11 @@ class rcube_message ) ]; + $this->mime = new rcube_mime($this->headers->charset); + $this->subject = str_replace("\n", '', $this->headers->get('subject')); + $from = $this->mime->decode_address_list($this->headers->from, 1); + $this->sender = current($from); + if (!empty($this->headers->structure)) { $this->get_mime_numbers($this->headers->structure); $this->parse_structure($this->headers->structure); @@ -132,11 +137,6 @@ class rcube_message $this->body = $this->storage->get_body($uid); } - $this->mime = new rcube_mime($this->headers->charset); - $this->subject = str_replace("\n", '', $this->headers->get('subject')); - $from = $this->mime->decode_address_list($this->headers->from, 1); - $this->sender = current($from); - // notify plugins and let them analyze this structured message object $this->app->plugins->exec_hook('message_load', ['object' => $this]); } |