Esempio n. 1
0
 /**
  * For XML-RPC - we want to check for enc / sigs
  *
  * @return $xml
  */
 protected function modify_payload()
 {
     global $HTTP_RAW_POST_DATA;
     $xml = null;
     // check for encryption and signatures
     if ($this->authmethod == WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN) {
         // we need the token so that we can find the key
         if (!($dbtoken = get_record('external_tokens', 'token', $this->token, 'tokentype', EXTERNAL_TOKEN_PERMANENT))) {
             // log failed login attempts
             throw new WebserviceAccessException(get_string('invalidtoken', 'auth.webservice'));
         }
         // is WS-Security active ?
         if ($dbtoken->wssigenc) {
             $this->publickey = $dbtoken->publickey;
         }
     } else {
         if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME) {
             // get the user
             $user = get_record('usr', 'username', $this->username);
             if (empty($user)) {
                 throw new WebserviceAccessException(get_string('wrongusernamepassword', 'auth.webservice'));
             }
             // get the institution from the external user
             $ext_user = get_record('external_services_users', 'userid', $user->id);
             if (empty($ext_user)) {
                 throw new WebserviceAccessException(get_string('wrongusernamepassword', 'auth.webservice'));
             }
             // is WS-Security active ?
             if ($ext_user->wssigenc) {
                 $this->publickey = $ext_user->publickey;
             }
         }
     }
     // only both if we can find a public key
     if (!empty($this->publickey)) {
         // A singleton provides our site's SSL info
         require_once get_config('docroot') . 'api/xmlrpc/lib.php';
         $HTTP_RAW_POST_DATA = file_get_contents('php://input');
         $openssl = OpenSslRepo::singleton();
         $payload = $HTTP_RAW_POST_DATA;
         $this->payload_encrypted = false;
         $this->payload_signed = false;
         try {
             $xml = new SimpleXMLElement($payload);
         } catch (Exception $e) {
             throw new XmlrpcServerException('Payload is not a valid XML document', 6001);
         }
         // Cascading switch. Kinda.
         try {
             if ($xml->getName() == 'encryptedMessage') {
                 $this->payload_encrypted = true;
                 $payload = xmlenc_envelope_strip($xml);
             }
             if ($xml->getName() == 'signedMessage') {
                 $this->payload_signed = true;
                 $payload = xmldsig_envelope_strip($xml);
             }
             $xml = $payload;
         } catch (CryptException $e) {
             if ($e->getCode() == 7025) {
                 // The key they used to contact us is old, respond with the new key correctly
                 // This sucks. Error handling of our mnet code needs to improve
                 ob_start();
                 xmlrpc_error($e->getMessage(), $e->getCode());
                 $response = ob_get_contents();
                 ob_end_clean();
                 // Sign and encrypt our response, even though we don't know if the
                 // request was signed and encrypted
                 $response = xmldsig_envelope($response);
                 $response = xmlenc_envelope($response, $this->publickey);
                 $xml = $response;
             }
         }
     }
     // if XML has been grabbed already then it must be turned into a request object
     if ($xml) {
         $request = new Zend_XmlRpc_Request();
         $request->loadXML($xml);
         $xml = $request;
     }
     return $xml;
 }
Esempio n. 2
0
// A singleton provides our site's SSL info
$openssl = OpenSslRepo::singleton();
$payload = $HTTP_RAW_POST_DATA;
$payload_encrypted = false;
$payload_signed = false;
try {
    $xml = new SimpleXMLElement($payload);
} catch (Exception $e) {
    throw new XmlrpcServerException('Payload is not a valid XML document', 6001);
}
// Cascading switch. Kinda.
try {
    if ($xml->getName() == 'encryptedMessage') {
        $payload_encrypted = true;
        $REMOTEWWWROOT = (string) $xml->wwwroot;
        $payload = xmlenc_envelope_strip($xml);
    }
    if ($xml->getName() == 'signedMessage') {
        $payload_signed = true;
        $REMOTEWWWROOT = (string) $xml->wwwroot;
        $payload = xmldsig_envelope_strip($xml);
    }
} catch (CryptException $e) {
    if ($e->getCode() == 7025) {
        // The key they used to contact us is old, respond with the new key correctly
        // This sucks. Error handling of our mnet code needs to improve
        ob_start();
        xmlrpc_error($e->getMessage(), $e->getCode());
        $response = ob_get_contents();
        ob_end_clean();
        // Sign and encrypt our response, even though we don't know if the
Esempio n. 3
0
 function send($wwwroot, $use_cached_peer = true)
 {
     $this->peer = get_peer($wwwroot, $use_cached_peer);
     $this->response = '';
     $URL = $this->peer->wwwroot . $this->peer->application->xmlrpcserverurl;
     $this->requesttext = xmlrpc_encode_request($this->method, $this->params, array("encoding" => "utf-8"));
     $this->signedrequest = xmldsig_envelope($this->requesttext);
     $this->encryptedrequest = xmlenc_envelope($this->signedrequest, $this->peer->certificate);
     $config = array(CURLOPT_URL => $URL, CURLOPT_TIMEOUT => $this->timeout, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_USERAGENT => 'Mahara', CURLOPT_POSTFIELDS => $this->encryptedrequest, CURLOPT_HTTPHEADER => array("Content-Type: text/xml charset=UTF-8", 'Expect: '), CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 0);
     $result = mahara_http_request($config);
     $timestamp_send = time();
     $this->rawresponse = $result->data;
     $response_code = $result->info['http_code'];
     $response_code_prefix = substr($response_code, 0, 1);
     if ('2' != $response_code_prefix) {
         if ('4' == $response_code_prefix) {
             throw new XmlrpcClientException('Client error code: ' . $response_code);
         } else {
             if ('5' == $response_code_prefix) {
                 throw new XmlrpcClientException('An error occurred at the remote server. Code: ' . $response_code);
             }
         }
     }
     $timestamp_receive = time();
     $remote_timestamp = null;
     $curl_errno = $result->errno;
     if ($curl_errno || $this->rawresponse == false) {
         throw new XmlrpcClientException('Curl error: ' . $curl_errno . ': ' . $result->error);
         return false;
     }
     try {
         $xml = new SimpleXMLElement(trim($this->rawresponse));
     } catch (Exception $e) {
         log_debug($this->rawresponse);
         throw new XmlrpcClientException('Payload is not a valid XML document (payload is above)', 6001);
     }
     try {
         if ($xml->getName() == 'encryptedMessage') {
             $payload_encrypted = true;
             $wwwroot = (string) $xml->wwwroot;
             // Strip encryption, using an older code is OK, because we're the client.
             // The server is able to respond with the correct key, be we're not
             $payload = xmlenc_envelope_strip($xml, true);
         }
         if ($xml->getName() == 'signedMessage') {
             $payload_signed = true;
             $remote_timestamp = $xml->timestamp;
             $payload = xmldsig_envelope_strip($xml);
         }
     } catch (CryptException $e) {
         throw new XmlrpcClientException("An error occurred while decrypting a message sent by {$wwwroot}. Unable to authenticate the user.");
     }
     if ($xml->getName() == 'methodResponse') {
         $this->response = xmlrpc_decode($payload);
         // 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;
         if (!empty($remote_timestamp)) {
             $remote_timestamp = $remote_timestamp - $hysteresis;
             $time_offset = $remote_timestamp - $timestamp_send;
             if ($time_offset > self::get_max_server_time_difference()) {
                 throw new XmlrpcClientException('Time drift (' . $margin_of_error . ', ' . $time_offset . ') is too large.');
             }
         }
         if (is_array($this->response) && array_key_exists('faultCode', $this->response)) {
             if ($this->response['faultCode'] == 7025) {
                 log_info('Remote application has sent us a new public key');
                 // The remote application sent back a new public key, the
                 // old one must have expired
                 if (array_key_exists('faultString', $this->response)) {
                     $details = openssl_x509_parse($this->response['faultString']);
                     if (isset($details['validTo_time_t'])) {
                         $updateobj = (object) array('publickey' => $this->response['faultString'], 'publickeyexpires' => $details['validTo_time_t']);
                         $whereobj = (object) array('wwwroot' => $wwwroot);
                         update_record('host', $updateobj, $whereobj);
                         log_info('New key has been imported. Valid until ' . date('Y/m/d h:i:s', $details['validTo_time_t']));
                         // Send request again. But don't use the cached
                         // peer, look it up again now we've changed the
                         // public key
                         $this->send($wwwroot, false);
                     } else {
                         throw new XmlrpcClientException('Could not parse new public key');
                     }
                 } else {
                     throw new XmlrpcClientException('Remote site claims to have sent a public key, but they LIE');
                 }
             }
             throw new XmlrpcClientException('Unknown error occurred: ' . $this->response['faultCode'] . ': ' . $this->response['faultString']);
         }
         // Clean up so object can be re-used.
         $this->requesttext = '';
         $this->signedrequest = '';
         $this->encryptedrequest = '';
         $this->params = array();
         $this->method = '';
         return true;
     } else {
         throw new XmlrpcClientException('Unrecognized XML document form: ' . $payload);
     }
 }
Esempio n. 4
0
 /**
  * For SOAP - we want to inspect for auth headers
  * and do decrypt / sigs
  *
  * @return $xml
  */
 protected function modify_payload()
 {
     $xml = null;
     // don't do any of this if we are in the WSDL phase
     if (param_boolean('wsdl', 0)) {
         return $xml;
     }
     // check for encryption and signatures
     if ($this->authmethod == WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN) {
         // we need the token so that we can find the key
         if (!($dbtoken = get_record('external_tokens', 'token', $this->token, 'tokentype', EXTERNAL_TOKEN_PERMANENT))) {
             // log failed login attempts
             throw new WebserviceAccessException(get_string('invalidtoken', 'auth.webservice'));
         }
         // is WS-Security active ?
         if ($dbtoken->wssigenc) {
             $this->publickey = $dbtoken->publickey;
         }
     } else {
         if ($this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME && !empty($this->username)) {
             // get the user
             $user = get_record('usr', 'username', $this->username);
             if (empty($user)) {
                 throw new WebserviceAccessException(get_string('wrongusernamepassword', 'auth.webservice'));
             }
             // get the institution from the external user
             $ext_user = get_record('external_services_users', 'userid', $user->id);
             if (empty($ext_user)) {
                 throw new WebserviceAccessException(get_string('wrongusernamepassword', 'auth.webservice'));
             }
             // is WS-Security active ?
             if ($ext_user->wssigenc) {
                 $this->publickey = $ext_user->publickey;
             }
         }
     }
     // only both if we can find a public key
     if (!empty($this->publickey)) {
         // A singleton provides our site's SSL info
         require_once get_config('docroot') . 'api/xmlrpc/lib.php';
         $HTTP_RAW_POST_DATA = file_get_contents('php://input');
         $openssl = OpenSslRepo::singleton();
         $payload = $HTTP_RAW_POST_DATA;
         $this->payload_encrypted = false;
         $this->payload_signed = false;
         try {
             $xml = new SimpleXMLElement($payload);
         } catch (Exception $e) {
             throw new XmlrpcServerException('Payload is not a valid XML document', 6001);
         }
         // Cascading switch. Kinda.
         try {
             if ($xml->getName() == 'encryptedMessage') {
                 $this->payload_encrypted = true;
                 $payload = xmlenc_envelope_strip($xml);
             }
             if ($xml->getName() == 'signedMessage') {
                 $this->payload_signed = true;
                 $signature = base64_decode($xml->Signature->SignatureValue);
                 $payload = base64_decode($xml->object);
                 $timestamp = $xml->timestamp;
                 // Does the signature match the data and the public cert?
                 $signature_verified = openssl_verify($payload, $signature, $this->publickey);
                 if ($signature_verified == 1) {
                     // Parse the XML
                     try {
                         $xml = new SimpleXMLElement($payload);
                     } catch (Exception $e) {
                         throw new MaharaException('Signed payload is not a valid XML document', 6007);
                     }
                 } else {
                     throw new MaharaException('An error occurred while trying to verify your message signature', 6004);
                 }
             }
             $xml = $payload;
         } catch (CryptException $e) {
             if ($e->getCode() == 7025) {
                 // The key they used to contact us is old, respond with the new key correctly
                 // This sucks. Error handling of our mnet code needs to improve
                 ob_start();
                 xmlrpc_error($e->getMessage(), $e->getCode());
                 $response = ob_get_contents();
                 ob_end_clean();
                 // Sign and encrypt our response, even though we don't know if the
                 // request was signed and encrypted
                 $response = xmldsig_envelope($response);
                 $response = xmlenc_envelope($response, $this->publickey);
                 $xml = $response;
             }
         }
     }
     // standard auth
     if (!isset($_REQUEST['wsusername']) && $this->authmethod == WEBSERVICE_AUTHMETHOD_USERNAME || !empty($this->publickey)) {
         // wsse auth
         // we may already have the xml if sig/enc
         if (empty($xml)) {
             $xml = file_get_contents('php://input');
         }
         $dom = new DOMDocument();
         if (strlen($xml) == 0 || !$dom->loadXML($xml)) {
             require_once 'Zend/Soap/Server/Exception.php';
             throw new Zend_Soap_Server_Exception('Invalid XML');
         } else {
             // now hunt for the user and password from the headers
             $xpath = new DOMXpath($dom);
             $xpath->registerNamespace('wsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd');
             if ($q = $xpath->query("//wsse:Security/wsse:UsernameToken/wsse:Username/text()", $dom)) {
                 if ($q->item(0)) {
                     $this->username = (string) $q->item(0)->data;
                     $this->password = (string) $xpath->query("//wsse:Security/wsse:UsernameToken/wsse:Password/text()", $dom)->item(0)->data;
                     $this->authmethod = WEBSERVICE_AUTHMETHOD_USERNAME;
                 }
             }
         }
     }
     return $xml;
 }