mnet: We now keep several generations of SSL keys
Author: Donal McMullan <[email protected]>
This commit is contained in:
+47
-2
@@ -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;
|
||||
|
||||
+4
-17
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+7
-1
@@ -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;
|
||||
|
||||
+17
-1
@@ -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);
|
||||
}
|
||||
|
||||
+21
-4
@@ -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;
|
||||
}
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user