ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Server.php
Go to the documentation of this file.
1 <?php
2 
9 {
15  private $domain;
16 
17 
23  private $server;
24 
25 
31  private $key;
32 
33 
41  private $cookieLifetime;
42 
43 
49  public function __construct($domain)
50  {
51  assert(is_string($domain));
52 
53  $cdcConfig = SimpleSAML_Configuration::getConfig('module_cdc.php');
54  $config = $cdcConfig->getConfigItem($domain, null);
55 
56  if ($config === null) {
57  throw new SimpleSAML_Error_Exception('Unknown CDC domain: ' . var_export($domain, true));
58  }
59 
60  $this->domain = $domain;
61  $this->server = $config->getString('server');
62  $this->key = $config->getString('key');
63  $this->cookieLifetime = $config->getInteger('cookie.lifetime', 0);
64 
65  if ($this->key === 'ExampleSharedKey') {
66  throw new SimpleSAML_Error_Exception('Key for CDC domain ' . var_export($domain, true) . ' not changed from default.');
67  }
68  }
69 
70 
76  public function sendRequest(array $request)
77  {
78  assert(isset($request['return']));
79  assert(isset($request['op']));
80 
81  $request['domain'] = $this->domain;
82  $this->send($this->server, 'CDCRequest', $request);
83  }
84 
85 
91  public function getResponse()
92  {
93  $response = self::get('CDCResponse');
94  if ($response === null) {
95  return null;
96  }
97 
98  if ($response['domain'] !== $this->domain) {
99  throw new SimpleSAML_Error_Exception('Response received from wrong domain.');
100  }
101 
102  $this->validate('CDCResponse');
103 
104  return $response;
105  }
106 
107 
111  public static function processRequest()
112  {
113  $request = self::get('CDCRequest');
114  if ($request === null) {
115  throw new SimpleSAML_Error_BadRequest('Missing "CDCRequest" parameter.');
116  }
117 
118  $domain = $request['domain'];
120 
121  $server->validate('CDCRequest');
122  $server->handleRequest($request);
123  }
124 
125 
131  private function handleRequest(array $request)
132  {
133  if (!isset($request['op'])) {
134  throw new SimpleSAML_Error_BadRequest('Missing "op" in CDC request.');
135  }
136  $op = (string)$request['op'];
137 
138  SimpleSAML\Logger::info('Received CDC request with "op": ' . var_export($op, true));
139 
140  if (!isset($request['return'])) {
141  throw new SimpleSAML_Error_BadRequest('Missing "return" in CDC request.');
142  }
143  $return = (string)$request['return'];
144 
145  switch ($op) {
146  case 'append':
147  $response = $this->handleAppend($request);
148  break;
149  case 'delete':
150  $response = $this->handleDelete($request);
151  break;
152  case 'read':
153  $response = $this->handleRead($request);
154  break;
155  default:
156  $response = 'unknown-op';
157  }
158 
159  if (is_string($response)) {
160  $response = array(
161  'status' => $response,
162  );
163  }
164 
165  $response['op'] = $op;
166  if (isset($request['id'])) {
167  $response['id'] = (string)$request['id'];
168  }
169  $response['domain'] = $this->domain;
170 
171  $this->send($return, 'CDCResponse', $response);
172  }
173 
174 
181  private function handleAppend(array $request)
182  {
183  if (!isset($request['entityID'])) {
184  throw new SimpleSAML_Error_BadRequest('Missing entityID in append request.');
185  }
186  $entityID = (string)$request['entityID'];
187 
188  $list = $this->getCDC();
189 
190  $prevIndex = array_search($entityID, $list, true);
191  if ($prevIndex !== false) {
192  unset($list[$prevIndex]);
193  }
194  $list[] = $entityID;
195 
196  $this->setCDC($list);
197 
198  return 'ok';
199  }
200 
201 
208  private function handleDelete(array $request)
209  {
210  $params = array(
211  'path' => '/',
212  'domain' => '.' . $this->domain,
213  'secure' => true,
214  'httponly' => false,
215  );
216 
217  \SimpleSAML\Utils\HTTP::setCookie('_saml_idp', null, $params, false);
218  return 'ok';
219  }
220 
221 
228  private function handleRead(array $request)
229  {
230  $list = $this->getCDC();
231 
232  return array(
233  'status' => 'ok',
234  'cdc' => $list,
235  );
236  }
237 
238 
245  private static function get($parameter)
246  {
247  assert(is_string($parameter));
248 
249  if (!isset($_REQUEST[$parameter])) {
250  return null;
251  }
252  $message = (string)$_REQUEST[$parameter];
253 
254  $message = @base64_decode($message);
255  if ($message === false) {
256  throw new SimpleSAML_Error_BadRequest('Error base64-decoding CDC message.');
257  }
258 
259  $message = @json_decode($message, true);
260  if ($message === false) {
261  throw new SimpleSAML_Error_BadRequest('Error json-decoding CDC message.');
262  }
263 
264  if (!isset($message['timestamp'])) {
265  throw new SimpleSAML_Error_BadRequest('Missing timestamp in CDC message.');
266  }
267  $timestamp = (int)$message['timestamp'];
268 
269  if ($timestamp + 60 < time()) {
270  throw new SimpleSAML_Error_BadRequest('CDC signature has expired.');
271  }
272  if ($timestamp - 60 > time()) {
273  throw new SimpleSAML_Error_BadRequest('CDC signature from the future.');
274  }
275 
276  if (!isset($message['domain'])) {
277  throw new SimpleSAML_Error_BadRequest('Missing domain in CDC message.');
278  }
279 
280  return $message;
281  }
282 
283 
291  private function validate($parameter)
292  {
293  assert(is_string($parameter));
294  assert(isset($_REQUEST[$parameter]));
295 
296  $message = (string)$_REQUEST[$parameter];
297 
298  if (!isset($_REQUEST['Signature'])) {
299  throw new SimpleSAML_Error_BadRequest('Missing Signature on CDC message.');
300  }
301  $signature = (string)$_REQUEST['Signature'];
302 
303  $cSignature = $this->calcSignature($message);
304  if ($signature !== $cSignature) {
305  throw new SimpleSAML_Error_BadRequest('Invalid signature on CDC message.');
306  }
307  }
308 
309 
317  private function send($to, $parameter, array $message)
318  {
319  assert(is_string($to));
320  assert(is_string($parameter));
321 
322  $message['timestamp'] = time();
323  $message = json_encode($message);
324  $message = base64_encode($message);
325 
326  $signature = $this->calcSignature($message);
327 
328  $params = array(
329  $parameter => $message,
330  'Signature' => $signature,
331  );
332 
333  $url = \SimpleSAML\Utils\HTTP::addURLParameters($to, $params);
334  if (strlen($url) < 2048) {
336  } else {
338  }
339  }
340 
341 
348  private function calcSignature($rawMessage)
349  {
350  assert(is_string($rawMessage));
351 
352  return sha1($this->key . $rawMessage . $this->key);
353  }
354 
355 
361  private function getCDC()
362  {
363  if (!isset($_COOKIE['_saml_idp'])) {
364  return array();
365  }
366 
367  $ret = (string)$_COOKIE['_saml_idp'];
368  $ret = explode(' ', $ret);
369  foreach ($ret as &$idp) {
370  $idp = base64_decode($idp);
371  if ($idp === false) {
372  // Not properly base64 encoded
373  SimpleSAML\Logger::warning('CDC - Invalid base64-encoding of CDC entry.');
374  return array();
375  }
376  }
377 
378  return $ret;
379  }
380 
381 
388  private function setCDC(array $list)
389  {
390  foreach ($list as &$value) {
391  $value = base64_encode($value);
392  }
393 
394  $cookie = implode(' ', $list);
395 
396  while (strlen($cookie) > 4000) {
397  // The cookie is too long. Remove the oldest elements until it is short enough
398  $tmp = explode(' ', $cookie, 2);
399  if (count($tmp) === 1) {
400  /*
401  * We are left with a single entityID whose base64
402  * representation is too long to fit in a cookie.
403  */
404  break;
405  }
406  $cookie = $tmp[1];
407  }
408 
409  $params = array(
410  'lifetime' => $this->cookieLifetime,
411  'path' => '/',
412  'domain' => '.' . $this->domain,
413  'secure' => true,
414  'httponly' => false,
415  );
416 
417  \SimpleSAML\Utils\HTTP::setCookie('_saml_idp', $cookie, $params, false);
418  }
419 }
$_COOKIE['client_id']
Definition: server.php:9
if(isset($_REQUEST['delete'])) $list
Definition: registry.php:41
$config
Definition: bootstrap.php:15
$cookieLifetime
The lifetime of our cookie, in seconds.
Definition: Server.php:41
foreach($paths as $path) $request
Definition: asyncclient.php:32
getCDC()
Get the IdP entities saved in the common domain cookie.
Definition: Server.php:361
__construct($domain)
Initialize a CDC server.
Definition: Server.php:49
static redirectTrustedURL($url, $parameters=array())
This function redirects to the specified URL without performing any security checks.
Definition: HTTP.php:959
handleDelete(array $request)
Handle a delete request.
Definition: Server.php:208
getResponse()
Parse and validate response received from a CDC server.
Definition: Server.php:91
calcSignature($rawMessage)
Calculate the signature on the given message.
Definition: Server.php:348
static info($string)
Definition: Logger.php:199
catch(Exception $e) $message
static setCookie($name, $value, $params=null, $throw=true)
Set a cookie.
Definition: HTTP.php:1104
static warning($string)
Definition: Logger.php:177
validate($parameter)
Helper function for validating the signature on a CDC message.
Definition: Server.php:291
setCDC(array $list)
Build a CDC cookie string.
Definition: Server.php:388
get(string $class_name)
static getConfig($filename='config.php', $configSet='simplesaml')
Load a configuration file from a configuration set.
sendRequest(array $request)
Send a request to this CDC server.
Definition: Server.php:76
static submitPOSTData($destination, $data)
Submit a POST form to a specific destination.
Definition: HTTP.php:1202
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:81
send($to, $parameter, array $message)
Helper function for sending CDC messages.
Definition: Server.php:317
$idp
Definition: prp.php:13
$ret
Definition: parser.php:6
$url
handleRead(array $request)
Handle a read request.
Definition: Server.php:228
$response
handleAppend(array $request)
Handle an append request.
Definition: Server.php:181
static processRequest()
Parse and process a CDC request.
Definition: Server.php:111
handleRequest(array $request)
Handle a parsed CDC requst.
Definition: Server.php:131