Example #1
0
 /**
  * Send the request to the server - decode and return the response
  *
  * @param  object   $mnet_peer      A mnet_peer object with details of the
  *                                  remote host we're connecting to
  * @return mixed                    A PHP variable, as returned by the
  *                                  remote function
  */
 function send($mnet_peer)
 {
     global $CFG, $MNET;
     $this->uri = $mnet_peer->wwwroot . $mnet_peer->application->xmlrpc_server_url;
     // Initialize with the target URL
     $ch = curl_init($this->uri);
     $system_methods = array('system/listMethods', 'system/methodSignature', 'system/methodHelp', 'system/listServices');
     if (in_array($this->method, $system_methods)) {
         // Executing any system method is permitted.
     } else {
         $id_list = $mnet_peer->id;
         if (!empty($CFG->mnet_all_hosts_id)) {
             $id_list .= ', ' . $CFG->mnet_all_hosts_id;
         }
         // At this point, we don't care if the remote host implements the
         // method we're trying to call. We just want to know that:
         // 1. The method belongs to some service, as far as OUR host knows
         // 2. We are allowed to subscribe to that service on this mnet_peer
         // Find methods that we subscribe to on this host
         $sql = "\n                SELECT\n                    r.id\n                FROM\n                    {$CFG->prefix}mnet_rpc r,\n                    {$CFG->prefix}mnet_service2rpc s2r,\n                    {$CFG->prefix}mnet_host2service h2s\n                WHERE\n                    r.xmlrpc_path = '{$this->method}' AND\n                    s2r.rpcid = r.id AND\n                    s2r.serviceid = h2s.serviceid AND\n                    h2s.subscribe = '1' AND\n                    h2s.hostid in ({$id_list})";
         if (!record_exists_sql($sql)) {
             global $USER;
             $this->error[] = '7:User with ID ' . $USER->id . ' attempted to call unauthorised method ' . $this->method . ' on host ' . $mnet_peer->wwwroot;
             return false;
         }
     }
     $this->requesttext = xmlrpc_encode_request($this->method, $this->params, array("encoding" => "utf-8", "escaping" => "markup"));
     $rq = $this->requesttext;
     $rq = mnet_sign_message($this->requesttext);
     $this->signedrequest = $rq;
     $rq = mnet_encrypt_message($rq, $mnet_peer->public_key);
     $this->encryptedrequest = $rq;
     curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
     curl_setopt($ch, CURLOPT_POST, true);
     curl_setopt($ch, CURLOPT_USERAGENT, 'Moodle');
     curl_setopt($ch, CURLOPT_POSTFIELDS, $rq);
     curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: text/xml charset=UTF-8"));
     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
     curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
     $timestamp_send = time();
     $this->rawresponse = curl_exec($ch);
     $timestamp_receive = time();
     if ($this->rawresponse === false) {
         $this->error[] = curl_errno($ch) . ':' . curl_error($ch);
         return false;
     }
     $this->rawresponse = trim($this->rawresponse);
     $mnet_peer->touch();
     $crypt_parser = new mnet_encxml_parser();
     $crypt_parser->parse($this->rawresponse);
     if ($crypt_parser->payload_encrypted) {
         $key = array_pop($crypt_parser->cipher);
         $data = array_pop($crypt_parser->cipher);
         $crypt_parser->free_resource();
         // Initialize payload var
         $payload = '';
         //                                          &$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
             $openssl_history = get_config('mnet', 'openssl_history');
             if (empty($openssl_history)) {
                 $openssl_history = array();
                 set_config('openssl_history', serialize($openssl_history), 'mnet');
             } else {
                 $openssl_history = unserialize($openssl_history);
             }
             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
                     break;
                 }
             }
         }
         if (!$isOpen) {
             trigger_error("None of our keys could open the payload from host {$mnet_peer->wwwroot} with id {$mnet_peer->id}.");
             $this->error[] = '3:No key match';
             return false;
         }
         if (strpos(substr($payload, 0, 100), '<signedMessage>')) {
             $sig_parser = new mnet_encxml_parser();
             $sig_parser->parse($payload);
         } else {
             $this->error[] = '2:Payload not signed: ' . $payload;
             return false;
         }
     } else {
         if (!empty($crypt_parser->remoteerror)) {
             $this->error[] = '4: remote server error: ' . $crypt_parser->remoteerror;
         } else {
             if (!empty($crypt_parser->error)) {
                 $crypt_parser_error = $crypt_parser->error[0];
                 $message = '3:XML Parse error in payload: ' . $crypt_parser_error['string'] . "\n";
                 if (array_key_exists('lineno', $crypt_parser_error)) {
                     $message .= 'At line number: ' . $crypt_parser_error['lineno'] . "\n";
                 }
                 if (array_key_exists('line', $crypt_parser_error)) {
                     $message .= 'Which reads: ' . $crypt_parser_error['line'] . "\n";
                 }
                 $this->error[] = $message;
             } else {
                 $this->error[] = '1:Payload not encrypted ';
             }
         }
         $crypt_parser->free_resource();
         return false;
     }
     // Margin of error is the time it took the request to complete.
     $margin_of_error = $timestamp_receive - $timestamp_send;
     // Guess the time gap between sending the request and the remote machine
     // executing the time() function. Marginally better than nothing.
     $hysteresis = $margin_of_error / 2;
     $remote_timestamp = $sig_parser->remote_timestamp - $hysteresis;
     $time_offset = $remote_timestamp - $timestamp_send;
     if ($time_offset > 0) {
         $threshold = get_config('mnet', 'drift_threshold');
         if (empty($threshold)) {
             // We decided 15 seconds was a pretty good arbitrary threshold
             // for time-drift between servers, but you can customize this in
             // the config_plugins table. It's not advised though.
             set_config('drift_threshold', 15, 'mnet');
             $threshold = 15;
         }
         if ($time_offset > $threshold) {
             $this->error[] = '6:Time gap with ' . $mnet_peer->name . ' (' . $time_offset . ' seconds) is greater than the permitted maximum of ' . $threshold . ' seconds';
             return false;
         }
     }
     $this->xmlrpcresponse = base64_decode($sig_parser->data_object);
     $this->response = xmlrpc_decode($this->xmlrpcresponse);
     curl_close($ch);
     // xmlrpc errors are pushed onto the $this->error stack
     if (is_array($this->response) && array_key_exists('faultCode', $this->response)) {
         // 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;
             if ($this->response['faultString'] == clean_param($this->response['faultString'], PARAM_PEM)) {
                 $record->public_key = $this->response['faultString'];
                 $details = openssl_x509_parse($record->public_key);
                 if (is_array($details) && isset($details['validTo_time_t'])) {
                     $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'];
                 }
             } else {
                 $this->error[] = $this->response['faultCode'] . " : " . $this->response['faultString'];
             }
         } else {
             if (!empty($CFG->mnet_rpcdebug)) {
                 $guidance = get_string('error' . $this->response['faultCode'], 'mnet');
             } else {
                 $guidance = '';
             }
             $this->error[] = $this->response['faultCode'] . " : " . $this->response['faultString'];
         }
     }
     // ok, it's signed, but is it signed with the right certificate ?
     // do this *after* we check for an out of date key
     $verified = openssl_verify($this->xmlrpcresponse, base64_decode($sig_parser->signature), $mnet_peer->public_key);
     if ($verified != 1) {
         $this->error[] = 'Invalid signature';
     }
     return empty($this->error);
 }
Example #2
0
function mnet_server_strip_signature($plaintextmessage) {
    $remoteclient = get_mnet_remote_client();
    $sig_parser = new mnet_encxml_parser();
    $sig_parser->parse($plaintextmessage);

    if ($sig_parser->signature == '') {
        return $plaintextmessage;
    }

    // Record that the request was signed in some way
    $remoteclient->was_signed();

    // Load any information we have about this mnet peer
    $remoteclient->set_wwwroot($sig_parser->remote_wwwroot);

    $payload = base64_decode($sig_parser->data_object);
    $signature = base64_decode($sig_parser->signature);
    $certificate = $remoteclient->public_key;

    // If we don't have any certificate for the host, don't try to check the signature
    // Just return the parsed request
    if ($certificate == false) {
        return $payload;
    }

    // Does the signature match the data and the public cert?
    $signature_verified = openssl_verify($payload, $signature, $certificate);
    if ($signature_verified == 0) {
        // $signature was not generated for $payload using $certificate
        // Get the key the remote peer is currently publishing:
        $currkey = mnet_get_public_key($remoteclient->wwwroot, $remoteclient->application);
        // If the key the remote peer is currently publishing is different to $certificate
        if($currkey != $certificate) {
            // if pushkey is already set, it means the request was encrypted to an old key
            // in mnet_server_strip_encryption.
            // if we call refresh_key() here before pushing out our new key,
            // and the other site ALSO has a new key,
            // we'll get into an infinite keyswap loop
            // so push just bail here, and push out the new key.
            // the next request will get through to refresh_key
            if ($remoteclient->pushkey) {
                return false;
            }
            // Try and get the server's new key through trusted means
            $remoteclient->refresh_key();
            // If we did manage to re-key, try to verify the signature again using the new public key.
            $certificate = $remoteclient->public_key;
            $signature_verified = openssl_verify($payload, $signature, $certificate);
        }
    }

    if ($signature_verified == 1) {
        $remoteclient->signature_verified();
        $remoteclient->touch();
    }

    $sig_parser->free_resource();

    return $payload;
}
Example #3
0
 /**
  * Send the request to the server - decode and return the response
  *
  * @param  object   $mnet_peer      A mnet_peer object with details of the
  *                                  remote host we're connecting to
  * @return mixed                    A PHP variable, as returned by the
  *                                  remote function
  */
 function send($mnet_peer)
 {
     global $CFG, $DB;
     if (!$this->permission_to_call($mnet_peer)) {
         mnet_debug("tried and wasn't allowed to call a method on {$mnet_peer->wwwroot}");
         return false;
     }
     $this->requesttext = xmlrpc_encode_request($this->method, $this->params, array("encoding" => "utf-8", "escaping" => "markup"));
     $this->signedrequest = mnet_sign_message($this->requesttext);
     $this->encryptedrequest = mnet_encrypt_message($this->signedrequest, $mnet_peer->public_key);
     $httprequest = $this->prepare_http_request($mnet_peer);
     curl_setopt($httprequest, CURLOPT_POSTFIELDS, $this->encryptedrequest);
     $timestamp_send = time();
     mnet_debug("about to send the curl request");
     $this->rawresponse = curl_exec($httprequest);
     mnet_debug("managed to complete a curl request");
     $timestamp_receive = time();
     if ($this->rawresponse === false) {
         $this->error[] = curl_errno($httprequest) . ':' . curl_error($httprequest);
         return false;
     }
     curl_close($httprequest);
     $this->rawresponse = trim($this->rawresponse);
     $mnet_peer->touch();
     $crypt_parser = new mnet_encxml_parser();
     $crypt_parser->parse($this->rawresponse);
     // If we couldn't parse the message, or it doesn't seem to have encrypted contents,
     // give the most specific error msg available & return
     if (!$crypt_parser->payload_encrypted) {
         if (!empty($crypt_parser->remoteerror)) {
             $this->error[] = '4: remote server error: ' . $crypt_parser->remoteerror;
         } else {
             if (!empty($crypt_parser->error)) {
                 $crypt_parser_error = $crypt_parser->error[0];
                 $message = '3:XML Parse error in payload: ' . $crypt_parser_error['string'] . "\n";
                 if (array_key_exists('lineno', $crypt_parser_error)) {
                     $message .= 'At line number: ' . $crypt_parser_error['lineno'] . "\n";
                 }
                 if (array_key_exists('line', $crypt_parser_error)) {
                     $message .= 'Which reads: ' . $crypt_parser_error['line'] . "\n";
                 }
                 $this->error[] = $message;
             } else {
                 $this->error[] = '1:Payload not encrypted ';
             }
         }
         $crypt_parser->free_resource();
         return false;
     }
     $key = array_pop($crypt_parser->cipher);
     $data = array_pop($crypt_parser->cipher);
     $crypt_parser->free_resource();
     // Initialize payload var
     $decryptedenvelope = '';
     //                                          &$decryptedenvelope
     $isOpen = openssl_open(base64_decode($data), $decryptedenvelope, base64_decode($key), $this->mnet->get_private_key());
     if (!$isOpen) {
         // Decryption failed... let's try our archived keys
         $openssl_history = get_config('mnet', 'openssl_history');
         if (empty($openssl_history)) {
             $openssl_history = array();
             set_config('openssl_history', serialize($openssl_history), 'mnet');
         } else {
             $openssl_history = unserialize($openssl_history);
         }
         foreach ($openssl_history as $keyset) {
             $keyresource = openssl_pkey_get_private($keyset['keypair_PEM']);
             $isOpen = openssl_open(base64_decode($data), $decryptedenvelope, base64_decode($key), $keyresource);
             if ($isOpen) {
                 // It's an older code, sir, but it checks out
                 break;
             }
         }
     }
     if (!$isOpen) {
         trigger_error("None of our keys could open the payload from host {$mnet_peer->wwwroot} with id {$mnet_peer->id}.");
         $this->error[] = '3:No key match';
         return false;
     }
     if (strpos(substr($decryptedenvelope, 0, 100), '<signedMessage>')) {
         $sig_parser = new mnet_encxml_parser();
         $sig_parser->parse($decryptedenvelope);
     } else {
         $this->error[] = '2:Payload not signed: ' . $decryptedenvelope;
         return false;
     }
     // Margin of error is the time it took the request to complete.
     $margin_of_error = $timestamp_receive - $timestamp_send;
     // Guess the time gap between sending the request and the remote machine
     // executing the time() function. Marginally better than nothing.
     $hysteresis = $margin_of_error / 2;
     $remote_timestamp = $sig_parser->remote_timestamp - $hysteresis;
     $time_offset = $remote_timestamp - $timestamp_send;
     if ($time_offset > 0) {
         $threshold = get_config('mnet', 'drift_threshold');
         if (empty($threshold)) {
             // We decided 15 seconds was a pretty good arbitrary threshold
             // for time-drift between servers, but you can customize this in
             // the config_plugins table. It's not advised though.
             set_config('drift_threshold', 15, 'mnet');
             $threshold = 15;
         }
         if ($time_offset > $threshold) {
             $this->error[] = '6:Time gap with ' . $mnet_peer->name . ' (' . $time_offset . ' seconds) is greater than the permitted maximum of ' . $threshold . ' seconds';
             return false;
         }
     }
     $this->xmlrpcresponse = base64_decode($sig_parser->data_object);
     $this->response = xmlrpc_decode($this->xmlrpcresponse);
     // xmlrpc errors are pushed onto the $this->error stack
     if (is_array($this->response) && array_key_exists('faultCode', $this->response)) {
         // 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)) {
             mnet_debug('recieved an old-key fault, so trying to get the new key and update our records');
             // If the new certificate doesn't come thru clean_param() unmolested, error out
             if ($this->response['faultString'] != clean_param($this->response['faultString'], PARAM_PEM)) {
                 $this->error[] = $this->response['faultCode'] . " : " . $this->response['faultString'];
             }
             $record = new stdClass();
             $record->id = $mnet_peer->id;
             $record->public_key = $this->response['faultString'];
             $details = openssl_x509_parse($record->public_key);
             if (!isset($details['validTo_time_t'])) {
                 $this->error[] = $this->response['faultCode'] . " : " . $this->response['faultString'];
             }
             $record->public_key_expires = $details['validTo_time_t'];
             $DB->update_record('mnet_host', $record);
             // Create a new peer object populated with the new info & try re-sending the request
             $rekeyed_mnet_peer = new mnet_peer();
             $rekeyed_mnet_peer->set_id($record->id);
             $rekeyed_mnet_peer->re_key = true;
             return $this->send($rekeyed_mnet_peer);
         }
         if (!empty($CFG->mnet_rpcdebug)) {
             if (get_string_manager()->string_exists('error' . $this->response['faultCode'], 'mnet')) {
                 $guidance = get_string('error' . $this->response['faultCode'], 'mnet');
             } else {
                 $guidance = '';
             }
         } else {
             $guidance = '';
         }
         $this->error[] = $this->response['faultCode'] . " : " . $this->response['faultString'] . "\n" . $guidance;
     }
     // ok, it's signed, but is it signed with the right certificate ?
     // do this *after* we check for an out of date key
     if (!openssl_verify($this->xmlrpcresponse, base64_decode($sig_parser->signature), $mnet_peer->public_key)) {
         $this->error[] = 'Invalid signature';
     }
     return empty($this->error);
 }
Example #4
0
/**
 * Strip the encryption (XML-ENC) and signature (XML-SIG) wrappers and return the XML-RPC payload
 *
 * IF COMMUNICATION TAKES PLACE OVER UNENCRYPTED HTTP:
 * The payload will have been encrypted with a symmetric key. This key will
 * itself have been encrypted using your public key. The key is decrypted using
 * your private key, and then used to decrypt the XML payload.
 *
 * IF COMMUNICATION TAKES PLACE OVER UNENCRYPTED HTTP *OR* ENCRYPTED HTTPS:
 * In either case, there will be an XML wrapper which contains your XML-RPC doc
 * as an object element, a signature for that doc, and various standards-
 * compliant info to aid in verifying the signature.
 *
 * This function parses the encryption wrapper, decrypts the contents, parses
 * the signature wrapper, and if the signature matches the payload, it returns
 * the payload, which should be an XML-RPC request.
 * If there is an error, or the signatures don't match, it echoes an XML-RPC
 * error and exits.
 *
 * See the W3C's {@link http://www.w3.org/TR/xmlenc-core/ XML Encryption Syntax and Processing}
 * and {@link http://www.w3.org/TR/2001/PR-xmldsig-core-20010820/ XML-Signature Syntax and Processing}
 * guidelines for more detail on the XML.
 *
 * -----XML-Envelope---------------------------------
 * |                                                |
 * |    Encrypted-Symmetric-key----------------     |
 * |    |_____________________________________|     |
 * |                                                |
 * |    Encrypted data-------------------------     |
 * |    |                                     |     |
 * |    |  -XML-Envelope------------------    |     |
 * |    |  |                             |    |     |
 * |    |  |  --Signature-------------   |    |     |
 * |    |  |  |______________________|   |    |     |
 * |    |  |                             |    |     |
 * |    |  |  --Signed-Payload--------   |    |     |
 * |    |  |  |                      |   |    |     |
 * |    |  |  |   XML-RPC Request    |   |    |     |
 * |    |  |  |______________________|   |    |     |
 * |    |  |                             |    |     |
 * |    |  |_____________________________|    |     |
 * |    |_____________________________________|     |
 * |                                                |
 * |________________________________________________|
 *
 * @uses $db
 * @param   string  $HTTP_RAW_POST_DATA   The XML that the client sent
 * @return  string                        The XMLRPC payload.
 */
function mnet_server_strip_wrappers($HTTP_RAW_POST_DATA)
{
    global $MNET, $MNET_REMOTE_CLIENT;
    if (isset($_SERVER)) {
        $crypt_parser = new mnet_encxml_parser();
        $crypt_parser->parse($HTTP_RAW_POST_DATA);
        // Make sure we know who we're talking to
        $host_record_exists = $MNET_REMOTE_CLIENT->set_wwwroot($crypt_parser->remote_wwwroot);
        if (false == $host_record_exists) {
            exit(mnet_server_fault(7020, 'wrong-wwwroot', $crypt_parser->remote_wwwroot));
        }
        if ($crypt_parser->payload_encrypted) {
            $key = array_pop($crypt_parser->cipher);
            // This key is Symmetric
            $data = array_pop($crypt_parser->cipher);
            $crypt_parser->free_resource();
            $payload = '';
            // Initialize payload var
            $push_current_key = false;
            // True if we need to push a fresh key to the peer
            //                                          &$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
                $openssl_history = get_config('mnet', 'openssl_history');
                if (empty($openssl_history)) {
                    $openssl_history = array();
                    set_config('openssl_history', serialize($openssl_history), 'mnet');
                } else {
                    $openssl_history = unserialize($openssl_history);
                }
                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
                        $push_current_key = true;
                        break;
                    }
                }
            }
            if (!$isOpen) {
                exit(mnet_server_fault(7023, 'encryption-invalid'));
            }
            if (strpos(substr($payload, 0, 100), '<signedMessage>')) {
                $MNET_REMOTE_CLIENT->was_signed();
                $sig_parser = new mnet_encxml_parser();
                $sig_parser->parse($payload);
            } else {
                exit(mnet_server_fault(7022, 'verifysignature-error'));
            }
        } else {
            exit(mnet_server_fault(7024, 'payload-not-encrypted'));
        }
        unset($payload);
        // if the peer used one of our public keys that have expired, we will
        // return a signed/encrypted error message with our new public key
        if ($push_current_key) {
            // NOTE: Here, we use the 'mnet_server_fault_xml' to avoid
            // get_string being called on our public_key
            exit(mnet_server_fault_xml(7025, $MNET->public_key, $keyresource));
        }
        /**
         * Get the certificate (i.e. public key) from the remote server.
         */
        $certificate = $MNET_REMOTE_CLIENT->public_key;
        if ($certificate == false) {
            exit(mnet_server_fault(709, 'nosuchpublickey'));
        }
        $payload = base64_decode($sig_parser->data_object);
        // Does the signature match the data and the public cert?
        $signature_verified = openssl_verify($payload, base64_decode($sig_parser->signature), $certificate);
        if ($signature_verified == 0) {
            // $signature was not generated for $payload using $certificate
            // Get the key the remote peer is currently publishing:
            $currkey = mnet_get_public_key($MNET_REMOTE_CLIENT->wwwroot, $MNET_REMOTE_CLIENT->application->xmlrpc_server_url);
            // If the key the remote peer is currently publishing is different to $certificate
            if ($currkey != $certificate) {
                // If we can't get the server's new key through trusted means, exit.
                if (!$MNET_REMOTE_CLIENT->refresh_key()) {
                    exit(mnet_server_fault(7026, 'verifysignature-invalid'));
                }
                // If we did manage to re-key, try to verify the signature again against the new public key.
                $certificate = $MNET_REMOTE_CLIENT->public_key;
                $signature_verified = openssl_verify($payload, base64_decode($sig_parser->signature), $certificate);
            }
        }
        if ($signature_verified == 1) {
            $MNET_REMOTE_CLIENT->touch();
        } elseif ($signature_verified == 0) {
            exit(mnet_server_fault(710, 'verifysignature-invalid'));
        } else {
            exit(mnet_server_fault(711, 'verifysignature-error'));
        }
        $sig_parser->free_resource();
        return $payload;
    } else {
        exit(mnet_server_fault(712, "phperror"));
    }
}
Example #5
0
function mnet_server_strip_signature($plaintextmessage)
{
    global $MNET, $MNET_REMOTE_CLIENT;
    $sig_parser = new mnet_encxml_parser();
    $sig_parser->parse($plaintextmessage);
    if ($sig_parser->signature == '') {
        return $plaintextmessage;
    }
    // Record that the request was signed in some way
    $MNET_REMOTE_CLIENT->was_signed();
    // Load any information we have about this mnet peer
    $MNET_REMOTE_CLIENT->set_wwwroot($sig_parser->remote_wwwroot);
    $payload = base64_decode($sig_parser->data_object);
    $signature = base64_decode($sig_parser->signature);
    $certificate = $MNET_REMOTE_CLIENT->public_key;
    // If we don't have any certificate for the host, don't try to check the signature
    // Just return the parsed request
    if ($certificate == false) {
        return $payload;
    }
    // Does the signature match the data and the public cert?
    $signature_verified = openssl_verify($payload, $signature, $certificate);
    if ($signature_verified == 0) {
        // $signature was not generated for $payload using $certificate
        // Get the key the remote peer is currently publishing:
        $currkey = mnet_get_public_key($MNET_REMOTE_CLIENT->wwwroot, $MNET_REMOTE_CLIENT->application);
        // If the key the remote peer is currently publishing is different to $certificate
        if ($currkey != $certificate) {
            // Try and get the server's new key through trusted means
            $MNET_REMOTE_CLIENT->refresh_key();
            // If we did manage to re-key, try to verify the signature again using the new public key.
            $certificate = $MNET_REMOTE_CLIENT->public_key;
            $signature_verified = openssl_verify($payload, $signature, $certificate);
        }
    }
    if ($signature_verified == 1) {
        $MNET_REMOTE_CLIENT->signature_verified();
        $MNET_REMOTE_CLIENT->touch();
    }
    $sig_parser->free_resource();
    return $payload;
}