Viewing file: Server.php (8.77 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
/** * CDC server class. * * @package simpleSAMLphp * @version $Id$ */ class sspmod_cdc_Server {
/** * The domain. * * @var string */ private $domain;
/** * The URL to the server. * * @var string */ private $server;
/** * Our shared key. * * @var string */ private $key;
/** * The lifetime of our cookie, in seconds. * * If this is 0, the cookie will expire when the browser is closed. * * @param int */ private $cookieLifetime;
/** * Initialize a CDC server. * * @param string $domain The domain we are a server for. */ public function __construct($domain) { assert('is_string($domain)');
$cdcConfig = SimpleSAML_Configuration::getConfig('module_cdc.php'); $config = $cdcConfig->getConfigItem($domain, NULL);
if ($config === NULL) { throw new SimpleSAML_Error_Exception('Unknown CDC domain: ' . var_export($domain, TRUE)); }
$this->domain = $domain; $this->server = $config->getString('server'); $this->key = $config->getString('key'); $this->cookieLifetime = $config->getInteger('cookie.lifetime', 0);
if ($this->key === 'ExampleSharedKey') { throw new SimpleSAML_Error_Exception('Key for CDC domain ' . var_export($domain, TRUE) . ' not changed from default.'); } }
/** * Send a request to this CDC server. * * @param array $request The CDC request. */ public function sendRequest(array $request) { assert('isset($request["return"])'); assert('isset($request["op"])');
$request['domain'] = $this->domain; $this->send($this->server, 'CDCRequest', $request); }
/** * Parse and validate response received from a CDC server. * * @return array|NULL The response, or NULL if no response is received. */ public function getResponse() {
$response = self::get('CDCResponse'); if ($response === NULL) { return NULL; }
if ($response['domain'] !== $this->domain) { throw new SimpleSAML_Error_Exception('Response received from wrong domain.'); }
$this->validate('CDCResponse');
return $response; }
/** * Parse and process a CDC request. */ public static function processRequest() { $request = self::get('CDCRequest'); if ($request === NULL) { throw new SimpleSAML_Error_BadRequest('Missing "CDCRequest" parameter.'); }
$domain = $request['domain']; $server = new sspmod_cdc_Server($domain);
$server->handleRequest($request); }
/** * Handle a parsed CDC requst. * * @param array $request */ private function handleRequest(array $request) {
if (!isset($request['op'])) { throw new SimpleSAML_Error_BadRequest('Missing "op" in CDC request.'); } $op = (string)$request['op'];
SimpleSAML_Logger::info('Received CDC request with "op": ' . var_export($op, TRUE));
if (!isset($request['return'])) { throw new SimpleSAML_Error_BadRequest('Missing "return" in CDC request.'); } $return = (string)$request['return'];
switch ($op) { case 'append': $response = $this->handleAppend($request); break; case 'delete': $response = $this->handleDelete($request); break; case 'read': $response = $this->handleRead($request); break; default: $response = 'unknown-op'; }
if (is_string($response)) { $response = array( 'status' => $response, ); }
$response['op'] = $op; if (isset($request['id'])) { $response['id'] = (string)$request['id']; } $response['domain'] = $this->domain;
$this->send($return, 'CDCResponse', $response); }
/** * Handle an append request. * * @param array $request The request. * @return array The response. */ private function handleAppend(array $request) {
if (!isset($request['entityID'])) { throw new SimpleSAML_Error_BadRequest('Missing entityID in append request.'); } $entityID = (string)$request['entityID'];
$list = $this->getCDC();
$prevIndex = array_search($entityID, $list, TRUE); if ($prevIndex !== FALSE) { unset($list[$prevIndex]); } $list[] = $entityID;
$this->setCDC($list);
return 'ok'; }
/** * Handle a delete request. * * @param array $request The request. * @return array The response. */ private function handleDelete(array $request) {
setcookie('_saml_idp', 'DELETE', time() - 86400 , '/', '.' . $this->domain, TRUE); return 'ok'; }
/** * Handle a read request. * * @param array $request The request. * @return array The response. */ private function handleRead(array $request) {
$list = $this->getCDC();
return array( 'status' => 'ok', 'cdc' => $list, ); }
/** * Helper function for parsing and validating a CDC message. * * @param string $parameter The name of the query parameter. * @return array|NULL The response, or NULL if no response is received. */ private static function get($parameter) { assert('is_string($parameter)');
if (!isset($_REQUEST[$parameter])) { return NULL; } $message = (string)$_REQUEST[$parameter];
$message = @base64_decode($message); if ($message === FALSE) { throw new SimpleSAML_Error_BadRequest('Error base64-decoding CDC message.'); }
$message = @json_decode($message, TRUE); if ($message === FALSE) { throw new SimpleSAML_Error_BadRequest('Error json-decoding CDC message.'); }
if (!isset($message['timestamp'])) { throw new SimpleSAML_Error_BadRequest('Missing timestamp in CDC message.'); } $timestamp = (int)$message['timestamp'];
if ($timestamp + 60 < time()) { throw new SimpleSAML_Error_BadRequest('CDC signature has expired.'); } if ($timestamp - 60 > time()) { throw new SimpleSAML_Error_BadRequest('CDC signature from the future.'); }
if (!isset($message['domain'])) { throw new SimpleSAML_Error_BadRequest('Missing domain in CDC message.'); }
return $message; }
/** * Helper function for validating the signature on a CDC message. * * Will throw an exception if the message is invalid. * * @param string $parameter The name of the query parameter. */ private function validate($parameter) { assert('is_string($parameter)'); assert('isset($_REQUEST[$parameter])');
$message = (string)$_REQUEST[$parameter];
if (!isset($_REQUEST['Signature'])) { throw new SimpleSAML_Error_BadRequest('Missing Signature on CDC message.'); } $signature = (string)$_REQUEST['Signature'];
$cSignature = $this->calcSignature($message); if ($signature !== $cSignature) { throw new SimpleSAML_Error_BadRequest('Invalid signature on CDC message.'); } }
/** * Helper function for sending CDC messages. * * @param string $to The URL the message should be delivered to. * @param string $parameter The query parameter the message should be sent in. * @param array $message The CDC message. */ private function send($to, $parameter, array $message) { assert('is_string($to)'); assert('is_string($parameter)');
$message['timestamp'] = time(); $message = json_encode($message); $message = base64_encode($message);
$signature = $this->calcSignature($message);
$params = array( $parameter => $message, 'Signature' => $signature, );
$url = SimpleSAML_Utilities::addURLparameter($to, $params); if (strlen($url) < 2048) { SimpleSAML_Utilities::redirect($url); } else { SimpleSAML_Utilities::postRedirect($to, $params); } }
/** * Calculate the signature on the given message. * * @param string $rawMessage The base64-encoded message. * @return string The signature. */ private function calcSignature($rawMessage) { assert('is_string($rawMessage)');
return sha1($this->key . $rawMessage . $this->key); }
/** * Get the IdP entities saved in the common domain cookie. * * @return array List of IdP entities. */ private function getCDC() {
if (!isset($_COOKIE['_saml_idp'])) { return array(); }
$ret = (string)$_COOKIE['_saml_idp']; $ret = explode(' ', $ret); foreach ($ret as &$idp) { $idp = base64_decode($idp); if ($idp === FALSE) { /* Not properly base64 encoded. */ SimpleSAML_Logger::warning('CDC - Invalid base64-encoding of CDC entry.'); return array(); } }
return $ret; }
/** * Build a CDC cookie string. * * @param array $list The list of IdPs. * @return string The CDC cookie value. */ function setCDC(array $list) {
foreach ($list as &$value) { $value = base64_encode($value); }
$cookie = implode(' ', $list);
while (strlen($cookie) > 4000) { /* The cookie is too long. Remove the oldest elements until it is short enough. */ $tmp = explode(' ', $cookie, 2); if (count($tmp) === 1) { /* * We are left with a single entityID whose base64 * representation is too long to fit in a cookie. */ break; } $cookie = $tmp[1]; }
if ($this->cookieLifetime === 0) { $expire = 0; } else { $expire = time() + $this->cookieLifetime; }
setcookie('_saml_idp', $cookie, $expire, '/', '.' . $this->domain, TRUE); }
}
|