]> git.mjollnir.org Git - moodle.git/commitdiff
mnet: We now keep several generations of SSL keys
authormartinlanghoff <martinlanghoff>
Thu, 4 Jan 2007 03:34:14 +0000 (03:34 +0000)
committermartinlanghoff <martinlanghoff>
Thu, 4 Jan 2007 03:34:14 +0000 (03:34 +0000)
Author: Donal McMullan <donal@catalyst.net.nz>

mnet/environment.php
mnet/lib.php
mnet/peer.php
mnet/xmlrpc/client.php
mnet/xmlrpc/server.php

index 07e656da257638e2327f9150c7a7978f3b448923..9b8129ac32c6f8f4d73471cd6b0873a89f48dbbc 100644 (file)
@@ -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;
index c28d3231464e4ffa15f128e396c6934259607b96..6ce50aa9782057ee61e54600452323aebe3390d0 100644 (file)
@@ -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;
 }
 
index 0f0628b8b32ad373b3d12e1675c269a18391acd7..028040e463d5ec8ce160e8a6fde252579fce1930 100644 (file)
@@ -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;
index ccc8cd9aa21ab2ceeca0196fbb4118dbafd005a9..245d3a9cb2e73b835100482c07d3520397d79722 100644 (file)
@@ -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);
     }
index cced068284317ba9830648d19451a5d00055e3dd..c8f39021aec01d51c3c2f39bdbd6d111eaeee55b 100644 (file)
@@ -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;
 }
 ?>