From: martinlanghoff Date: Thu, 4 Jan 2007 03:34:14 +0000 (+0000) Subject: mnet: We now keep several generations of SSL keys X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=735c7beb0ad72687abcdb47f5fd1712276164be7;p=moodle.git mnet: We now keep several generations of SSL keys Author: Donal McMullan --- diff --git a/mnet/environment.php b/mnet/environment.php index 07e656da25..9b8129ac32 100644 --- a/mnet/environment.php +++ b/mnet/environment.php @@ -72,17 +72,62 @@ class mnet_environment { } function get_keypair() { + // We don't generate keys on install/upgrade because we want the USER + // record to have an email address, city and country already. + if (!empty($_SESSION['upgraderunning'])) return true; if (!empty($this->keypair)) return true; - if ($result = get_record_select('config', " name = 'openssl'")) { - $this->keypair = unserialize($result->value); + + $this->keypair = array(); + $keypair = get_field('config_plugins', 'value', 'plugin', 'mnet', 'name', 'openssl'); + + if (!empty($keypair)) { + // Explode/Implode is faster than Unserialize/Serialize + $this->keypair = explode('@@@@@@@@', $keypair); + } + + if ($this->public_key_expires > time()) { $this->keypair['privatekey'] = openssl_pkey_get_private($this->keypair['keypair_PEM']); $this->keypair['publickey'] = openssl_pkey_get_public($this->keypair['certificate']); } else { + // Key generation/rotation + + // 1. Archive the current key (if there is one). + $result = get_field('config_plugins', 'value', 'plugin', 'mnet', 'name', 'openssl_history'); + if(empty($result)) { + set_config('openssl_history', serialize(array()), 'mnet'); + $openssl_history = array(); + } else { + $openssl_history = unserialize($result); + } + + if(count($this->keypair)) { + $this->keypair['expires'] = $this->public_key_expires; + array_unshift($openssl_history, $this->keypair); + } + + // 2. How many old keys do we want to keep? Use array_slice to get + // rid of any we don't want + $openssl_generations = get_field('config_plugins', 'value', 'plugin', 'mnet', 'name', 'openssl_generations'); + if(empty($openssl_generations)) { + set_config('openssl_generations', 3, 'mnet'); + $openssl_generations = 3; + } + + if(count($openssl_history) > $openssl_generations) { + $openssl_history = array_slice($openssl_history, 0, $openssl_generations); + } + + set_config('openssl_history', serialize($openssl_history), 'mnet'); + + // 3. Generate fresh keys + $this->keypair = array(); $this->keypair = mnet_generate_keypair(); $this->public_key = $this->keypair['certificate']; $details = openssl_x509_parse($this->public_key); $this->public_key_expires = $details['validTo_time_t']; + set_config('openssl', implode('@@@@@@@@', $this->keypair); + update_record('mnet_host', $this); } return true; diff --git a/mnet/lib.php b/mnet/lib.php index c28d323146..6ce50aa978 100644 --- a/mnet/lib.php +++ b/mnet/lib.php @@ -245,8 +245,8 @@ function mnet_get_keypair() { global $CFG; static $keypair = null; if (!is_null($keypair)) return $keypair; - if ($result = get_record_select('config', " name = 'openssl'")) { - $keypair = unserialize($result->value); + if ($result = get_field('config_plugins', 'value', 'plugin', 'mnet', 'name', 'openssl')) { + $keypair = explode('@@@@@@@@', $keypair); $keypair['privatekey'] = openssl_pkey_get_private($keypair['keypair_PEM']); $keypair['publickey'] = openssl_pkey_get_public($keypair['certificate']); return $keypair; @@ -268,7 +268,7 @@ function mnet_get_keypair() { * @param array $dn The distinguished name of the server * @return string The signature over that text */ -function mnet_generate_keypair($dn = null) { +function mnet_generate_keypair($dn = null, $days=28) { global $CFG, $USER; $host = strtolower($CFG->wwwroot); $host = ereg_replace("^http(s)?://",'',$host); @@ -313,7 +313,7 @@ function mnet_generate_keypair($dn = null) { $new_key = openssl_pkey_new(); $csr_rsc = openssl_csr_new($dn, $new_key, array('private_key_bits',2048)); - $selfSignedCert = openssl_csr_sign($csr_rsc, null, $new_key, 365); + $selfSignedCert = openssl_csr_sign($csr_rsc, null, $new_key, $days); unset($csr_rsc); // Free up the resource // We export our self-signed certificate to a string. @@ -326,19 +326,6 @@ function mnet_generate_keypair($dn = null) { openssl_pkey_free($new_key); unset($new_key); // Free up the resource - $record = new stdClass(); - $record->name = 'openssl'; - // Normally we would just serialize the object, but that's not - // working, nor behaving as the docs suggest it should. Casting the - // object to an array and serializing the array works fine. - $record->value = serialize($keypair); - - insert_record('config', $record); - // unset $record - - $keypair['privatekey'] = openssl_pkey_get_private($keypair['keypair_PEM']); - $keypair['publickey'] = openssl_pkey_get_public($keypair['certificate']); - return $keypair; } diff --git a/mnet/peer.php b/mnet/peer.php index 0f0628b8b3..028040e463 100644 --- a/mnet/peer.php +++ b/mnet/peer.php @@ -205,7 +205,13 @@ class mnet_peer { return false; } - // PRIVATE METHOD + /** + * Several methods can be used to get an 'mnet_host' record. They all then + * send it to this private method to populate this object's attributes. + * + * @param object $hostinfo A database record from the mnet_host table + * @return void + */ function populate($hostinfo) { $this->id = $hostinfo->id; $this->wwwroot = $hostinfo->wwwroot; diff --git a/mnet/xmlrpc/client.php b/mnet/xmlrpc/client.php index ccc8cd9aa2..245d3a9cb2 100644 --- a/mnet/xmlrpc/client.php +++ b/mnet/xmlrpc/client.php @@ -217,7 +217,23 @@ class mnet_xmlrpc_client { // xmlrpc errors are pushed onto the $this->error stack if (isset($this->response['faultCode'])) { - $this->error[] = $this->response['faultCode'] . " : " . $this->response['faultString']; + // The faultCode 7025 means we tried to connect with an old SSL key + // The faultString is the new key - let's save it and try again + // The re_key attribute stops us from getting into a loop + if($this->response['faultCode'] == 7025 && empty($mnet_peer->re_key)) { + $record = new stdClass(); + $record->id = $mnet_peer->id; + $record->public_key = $this->response['faultString']; + $details = openssl_x509_parse($record->public_key); + $record->public_key_expires = $details['validTo_time_t']; + update_record('mnet_host', $record); + $mnet_peer2 = new mnet_peer(); + $mnet_peer2->set_id($record->id); + $mnet_peer2->re_key = true; + $this->send($mnet_peer2); + } else { + $this->error[] = $this->response['faultCode'] . " : " . $this->response['faultString']; + } } return empty($this->error); } diff --git a/mnet/xmlrpc/server.php b/mnet/xmlrpc/server.php index cced068284..c8f39021ae 100644 --- a/mnet/xmlrpc/server.php +++ b/mnet/xmlrpc/server.php @@ -123,7 +123,7 @@ function mnet_server_strip_wrappers($HTTP_RAW_POST_DATA) { if ($crypt_parser->payload_encrypted) { - $key = array_pop($crypt_parser->cipher); + $key = array_pop($crypt_parser->cipher); // This key is Symmetric $data = array_pop($crypt_parser->cipher); $crypt_parser->free_resource(); @@ -134,6 +134,24 @@ function mnet_server_strip_wrappers($HTTP_RAW_POST_DATA) { // &$payload $isOpen = openssl_open(base64_decode($data), $payload, base64_decode($key), $MNET->get_private_key()); + if (!$isOpen) { + // Decryption failed... let's try our archived keys + $result = get_config('mnet', 'openssl_history'); + if(empty($result)) { + set_config('openssl_history', serialize(array()), 'mnet'); + $result = get_config('mnet', 'openssl_history'); + } + $openssl_history = unserialize($result->value); + foreach($openssl_history as $keyset) { + $keyresource = openssl_pkey_get_private($keyset['keypair_PEM']); + $isOpen = openssl_open(base64_decode($data), $payload, base64_decode($key), $keyresource); + if ($isOpen) { + // It's an older code, sir, but it checks out + exit(mnet_server_fault(7025, $MNET->public_key)); + } + } + } + if (!$isOpen) { exit(mnet_server_fault(7023, 'encryption-invalid')); } @@ -653,7 +671,7 @@ function mnet_server_invoke_method($includefile, $methodname, $method, $payload, } function mnet_keyswap($function, $params) { - global $CFG; + global $CFG, $MNET; $return = array(); if (!empty($CFG->mnet_register_allhosts)) { @@ -663,7 +681,6 @@ function mnet_keyswap($function, $params) { $mnet_peer->commit(); } } - $keypair = mnet_get_keypair(); - return $keypair['certificate']; + return $MNET->public_key; } ?>