diff --git a/modules/casserver/www/cas.php b/modules/casserver/www/cas.php index 42efdf3fea09f818768ddccf8adc488577c5e9b6..1da904b24f796cd2c6d7202fb6d3708a5d071c8e 100644 --- a/modules/casserver/www/cas.php +++ b/modules/casserver/www/cas.php @@ -1,17 +1,36 @@ <?php /* - * Frontend for login.php, validate.php and serviceValidate.php. It allows them to be called + * Frontend for login.php, proxy.php, validate.php and serviceValidate.php. It allows them to be called * as cas.php/login, cas.php/validate and cas.php/serviceValidate and is meant for clients * like phpCAS which expects one configured prefix which it appends login, validate and * serviceValidate to. + * + * This version supports CAS proxying. As SSP controls the user session (TGT in CAS parlance) + * and the CASServer as a backend/proxy server is not aware of termination of the session the Proxy- + * Granting-Tickets (PGT) issued have a very short ttl - pt. 60 secs. + * + * ServiceTickets (SP) and ProxyTickets (PT) now have a 5 secs ttl. + * + * Proxyed services (targetService) shall be present in the legal_service_urls config. + * */ -$validFunctions = array('login', 'validate', 'serviceValidate'); +$validFunctions = array( + 'login' => 'login', + 'proxy' => 'proxy', + 'validate' => 'serviceValidate', + 'serviceValidate' => 'serviceValidate', + 'proxyValidate' => 'serviceValidate' +); + $function = substr($_SERVER['PATH_INFO'], 1); -if (!in_array($function, $validFunctions, TRUE)) { + +if (!isset($validFunctions[$function])) { throw new SimpleSAML_Error_NotFound('Not a valid function for cas.php.'); } -include($function.".php"); +include($validFunctions[$function].".php"); + +?> diff --git a/modules/casserver/www/login.php b/modules/casserver/www/login.php index 34fd04d5c893f4f021c40aa9b7352c72303fb4f6..c57183093e1263e2638af00e5c8ee1ea20648540 100644 --- a/modules/casserver/www/login.php +++ b/modules/casserver/www/login.php @@ -1,4 +1,5 @@ <?php +require 'tickets.php'; /* * Incomming parameters: @@ -8,55 +9,50 @@ * */ - if (!array_key_exists('service', $_GET)) throw new Exception('Required URL query parameter [service] not provided. (CAS Server)'); $service = $_GET['service']; -$renew = FALSE; -$gateway = FALSE; - -if (array_key_exists('renew', $_GET)) { - $renew = TRUE; -} - -if (array_key_exists('gateway', $_GET)) { - $gateway = TRUE; - throw new Exception('CAS gateway to SAML IsPassive: Not yet implemented properly.'); -} +$forceAuthn =isset($_GET['renew']) && $_GET['renew']; +$isPassive = isset($_GET['gateway']) && $_GET['gateway']; - - - -/* Load simpleSAMLphp, configuration and metadata */ $config = SimpleSAML_Configuration::getInstance(); $casconfig = SimpleSAML_Configuration::getConfig('module_casserver.php'); $session = SimpleSAML_Session::getInstance(); - $legal_service_urls = $casconfig->getValue('legal_service_urls'); if (!checkServiceURL($service, $legal_service_urls)) throw new Exception('Service parameter provided to CAS server is not listed as a legal service: [service] = ' . $service); $auth = $casconfig->getValue('auth', 'saml2'); if (!in_array($auth, array('saml2', 'shib13'))) - throw new Exception('CAS Service configured to use [auth] = ' . $auth . ' only [saml2,shib13] is legal.'); - -if (!$session->isValid($auth) ) { - SimpleSAML_Utilities::redirect( - '/' . $config->getBaseURL() . $auth . '/sp/initSSO.php', - array('RelayState' => SimpleSAML_Utilities::selfURL() ) + throw new Exception('CAS Service configured to use [auth] = ' . $auth . ' only [saml2,shib13] is legal.'); + +/* +if (!$session->isValid($auth)) { + $url = SimpleSAML_Utilities::selfURL(); + $hints = array( + SimpleSAML_Auth_State::RESTART => $url, + 'ForceAuthn' => $forceAuthn, + 'isPassive' => $isPassive, ); + SimpleSAML_Auth_Default::initLogin($auth, $url, $url, $hints); } + $attributes = $session->getAttributes(); +*/ -$path = $casconfig->resolvePath($casconfig->getValue('ticketcache', 'ticketcache')); -$ticket = str_replace( '_', 'ST-', SimpleSAML_Utilities::generateID() ); -storeTicket($ticket, $path, $attributes); +$attributes = array('mail' => array('freek@ruc.dk'), 'anton' => array('banton')); -// $test = retrieveTicket($ticket, $path); +$path = $casconfig->resolvePath($casconfig->getValue('ticketcache', '/tmp')); +$ticket = str_replace( '_', 'ST-', SimpleSAML_Utilities::generateID() ); +storeTicket($ticket, $path, array('service' => $service, + 'forceAuthn' => $forceAuthn, + 'attributes' => $attributes, + 'proxies' => array(), + 'validbefore' => time() + 5)); SimpleSAML_Utilities::redirect( SimpleSAML_Utilities::addURLparameter($service, @@ -64,42 +60,4 @@ SimpleSAML_Utilities::redirect( ) ); - - -function storeTicket($ticket, $path, &$value ) { - - if (!is_dir($path)) - throw new Exception('Directory for CAS Server ticket storage [' . $path . '] does not exists. '); - - if (!is_writable($path)) - throw new Exception('Directory for CAS Server ticket storage [' . $path . '] is not writable. '); - - $filename = $path . '/' . $ticket; - file_put_contents($filename, serialize($value)); -} - -function retrieveTicket($ticket, $path) { - - if (!is_dir($path)) - throw new Exception('Directory for CAS Server ticket storage [' . $path . '] does not exists. '); - - - $filename = $path . '/' . $ticket; - return unserialize(file_get_contents($filename)); -} - - - -function checkServiceURL($service, array $legal_service_urls) { - foreach ($legal_service_urls AS $legalurl) { - if (strpos($service, $legalurl) === 0) return TRUE; - } - return FALSE; -} - - - - - - ?> \ No newline at end of file diff --git a/modules/casserver/www/proxy.php b/modules/casserver/www/proxy.php new file mode 100644 index 0000000000000000000000000000000000000000..0d21cfd77cd3c5f507a5ba8c25fdd02d000d1826 --- /dev/null +++ b/modules/casserver/www/proxy.php @@ -0,0 +1,55 @@ +<?php +require 'tickets.php'; + +/* + * Incomming parameters: + * targetService + * ptg + * + */ + +if (array_key_exists('targetService', $_GET)) { + $targetService = $_GET['targetService']; + $pgt = $_GET['pgt']; +} else { + throw new Exception('Required URL query parameter [targetService] not provided. (CAS Server)'); +} + +$casconfig = SimpleSAML_Configuration::getConfig('module_casserver.php'); + +$legal_service_urls = $casconfig->getValue('legal_service_urls'); + +if (!checkServiceURL($targetService, $legal_service_urls)) + throw new Exception('Service parameter provided to CAS server is not listed as a legal service: [service] = ' . $service); + +$path = $casconfig->resolvePath($casconfig->getValue('ticketcache', 'ticketcache')); + +$ticket = retrieveTicket($pgt, $path, false); +if ($ticket['validbefore'] > time()) { + $pt = str_replace( '_', 'PT-', SimpleSAML_Utilities::generateID() ); + storeTicket($pt, $path, array( + 'service' => $targetService, + 'forceAuthn' => false, + 'attributes' => $ticket['attributes'], + 'proxies' => $ticket['proxies'], + 'validbefore' => time() + 5) + ); + +print <<<eox +<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> + <cas:proxySuccess> + <cas:proxyTicket>$pt</cas:proxyTicket> + </cas:proxySuccess> +</cas:serviceResponse> +eox; +} else { +print <<<eox +<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> + <cas:proxyFailure code="INVALID_REQUEST"> + Proxygranting ticket to old - ssp casserver only supports shortlived (30 secs) pgts. + </cas:proxyFailure> +</cas:serviceResponse> +eox; +} + +?> \ No newline at end of file diff --git a/modules/casserver/www/serviceValidate.php b/modules/casserver/www/serviceValidate.php index a80922557ee1fcf5835c20d9cbdd0fd74d68e363..53c712114d865e5c2b9c577b239a8bb475203291 100644 --- a/modules/casserver/www/serviceValidate.php +++ b/modules/casserver/www/serviceValidate.php @@ -1,4 +1,8 @@ <?php +require 'tickets.php'; + +# set manually if called directly - ie not included from validate.php or cas.php +if (!$function) $function = 'serviceValidate'; /* * Incomming parameters: @@ -8,107 +12,90 @@ * */ - -if (!array_key_exists('service', $_GET)) +if (array_key_exists('service', $_GET)) { + $service = $_GET['service']; + $ticket = $_GET['ticket']; + $forceAuthn = isset($_GET['renew']) && $_GET['renew']; +} else { throw new Exception('Required URL query parameter [service] not provided. (CAS Server)'); - -$service = $_GET['service']; - -if (!array_key_exists('ticket', $_GET)) - throw new Exception('Required URL query parameter [ticket] not provided. (CAS Server)'); - -$ticket = $_GET['ticket']; - -$renew = FALSE; - -if (array_key_exists('renew', $_GET)) { - $renew = TRUE; } - - try { - /* Load simpleSAMLphp, configuration and metadata */ +/* Load simpleSAMLphp, configuration and metadata */ $casconfig = SimpleSAML_Configuration::getConfig('module_casserver.php'); - $path = $casconfig->resolvePath($casconfig->getValue('ticketcache', 'ticketcache')); - $ticketcontent = retrieveTicket($ticket, $path); $usernamefield = $casconfig->getValue('attrname', 'eduPersonPrincipalName'); - $dosendattributes = $casconfig->getValue('attributes', FALSE);; + $dosendattributes = $casconfig->getValue('attributes', FALSE); - if (array_key_exists($usernamefield, $ticketcontent)) { - returnResponse('YES', $ticketcontent[$usernamefield][0], $dosendattributes ? $ticketcontent : array()); + $attributes = $ticketcontent['attributes']; + + $pgtiouxml = ""; + + if ($ticketcontent['service'] == $service + && $ticketcontent['forceAuthn'] == $forceAuthn + && array_key_exists($usernamefield, $attributes) + && $ticketcontent['validbefore'] > time()) { + + if (isset($_GET['pgtUrl'])) { + $pgtUrl = $_GET['pgtUrl']; + $pgtiou = str_replace( '_', 'PGTIOU-', SimpleSAML_Utilities::generateID()); + $pgt = str_replace( '_', 'PGT-', SimpleSAML_Utilities::generateID()); + $content = array( + 'attributes' => $attributes, + 'forceAuthn' => false, + 'proxies' => array_merge(array($service), $ticketcontent['proxies']), + 'validbefore' => time() + 60); + file_get_contents($pgtUrl . '?pgtIou=' . $pgtiou . '&pgtId=' . $pgt); + storeTicket($pgt, $path, $content); + $pgtiouxml = "\n<cas:proxyGrantingTicket>$pgtiou</cas:proxyGrantingTicket>\n"; + } + + $proxiesxml = join("\n", array_map(create_function('$a', 'return "<cas:proxy>$a</cas:proxy>";'), $ticketcontent['proxies'])); + if ($proxiesxml) $proxiesxml = "<cas:proxies>\n$proxiesxml\n</cas:proxies>\n"; + returnResponse('YES', $function, $attributes[$usernamefield][0], $dosendattributes ? $attributes : array(), $pgtiouxml.$proxiesxml); } else { - returnResponse('NO'); + returnResponse('NO', $function); } } catch (Exception $e) { - - returnResponse('NO', $e->getMessage()); + returnResponse('NO', $function, $e->getMessage()); } -function returnResponse($value, $content = '', $attributes = array()) { - if ($value === 'YES') { - $attributesxml = ""; - foreach ($attributes as $attributename => $attributelist) { - $attr = htmlspecialchars($attributename); - foreach ($attributelist as $attributevalue) { - $attributesxml .= "<cas:$attr>" . htmlspecialchars($attributevalue) . "</cas:$attr>\n"; +function returnResponse($value, $function, $usrname = '', $attributes = array(), $xtraxml = "") { + if ($value === 'YES') { + if ($function == 'serviceValidate') { + $attributesxml = ""; + foreach ($attributes as $attributename => $attributelist) { + $attr = htmlspecialchars($attributename); + foreach ($attributelist as $attributevalue) { + $attributesxml .= "<cas:$attr>" . htmlspecialchars($attributevalue) . "</cas:$attr>\n"; + } } - } - if (sizeof($attributes)) $attributesxml = '<cas:attributes>' . $attributesxml . '</cas:attributes>'; - echo '<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"> - <cas:authenticationSuccess> - <cas:user>' . htmlspecialchars($content) . '</cas:user>' . - $attributesxml . - '</cas:authenticationSuccess> + if (sizeof($attributes)) $attributesxml = "<cas:attributes>\n" . $attributesxml . "</cas:attributes>\n"; + echo '<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"> +<cas:authenticationSuccess> +<cas:user>' . htmlspecialchars($usrname) . '</cas:user>' . + $xtraxml . + $attributesxml . + '</cas:authenticationSuccess> </cas:serviceResponse>'; - + } else { + echo 'YES' . "\n" . $username; + } } else { - echo '<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"> - <cas:authenticationFailure code="..."> - ' . $content . ' - </cas:authenticationFailure> + if ($function == 'serviceValidate') { + echo '<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"> +<cas:authenticationFailure code=""> +</cas:authenticationFailure> </cas:serviceResponse>'; - } -} + } else { + echo 'NO'; - -function storeTicket($ticket, $path, &$value ) { - - if (!is_dir($path)) - throw new Exception('Directory for CAS Server ticket storage [' . $path . '] does not exists. '); - - if (!is_writable($path)) - throw new Exception('Directory for CAS Server ticket storage [' . $path . '] is not writable. '); - - $filename = $path . '/' . $ticket; - file_put_contents($filename, serialize($value)); -} - -function retrieveTicket($ticket, $path) { - - if (!preg_match('/^ST-?[a-zA-Z0-9]+$/D', $ticket)) throw new Exception('Invalid characters in ticket'); - - if (!is_dir($path)) - throw new Exception('Directory for CAS Server ticket storage [' . $path . '] does not exists. '); - - $filename = $path . '/' . $ticket; - - if (!file_exists($filename)) - throw new Exception('Could not find ticket'); - - $content = file_get_contents($filename); - - unlink($filename); - - return unserialize($content); + } + } } - - - ?> \ No newline at end of file diff --git a/modules/casserver/www/tickets.php b/modules/casserver/www/tickets.php new file mode 100644 index 0000000000000000000000000000000000000000..165bcc77cebbe5405b2ecba3642e507528e9ba92 --- /dev/null +++ b/modules/casserver/www/tickets.php @@ -0,0 +1,43 @@ +<?php + +function storeTicket($ticket, $path, $value ) { + + if (!is_dir($path)) + throw new Exception('Directory for CAS Server ticket storage [' . $path . '] does not exists. '); + + if (!is_writable($path)) + throw new Exception('Directory for CAS Server ticket storage [' . $path . '] is not writable. '); + + $filename = $path . '/' . $ticket; + file_put_contents($filename, serialize($value)); +} + +function retrieveTicket($ticket, $path, $unlink = true) { + + if (!preg_match('/^(ST|PT|PGT)-?[a-zA-Z0-9]+$/D', $ticket)) throw new Exception('Invalid characters in ticket'); + + if (!is_dir($path)) + throw new Exception('Directory for CAS Server ticket storage [' . $path . '] does not exists. '); + + $filename = $path . '/' . $ticket; + + if (!file_exists($filename)) + throw new Exception('Could not find ticket'); + + $content = file_get_contents($filename); + + if ($unlink) { + unlink($filename); + } + + return unserialize($content); +} + +function checkServiceURL($service, array $legal_service_urls) { + foreach ($legal_service_urls AS $legalurl) { + if (strpos($service, $legalurl) === 0) return TRUE; + } + return FALSE; +} + +?> \ No newline at end of file diff --git a/modules/casserver/www/validate.php b/modules/casserver/www/validate.php index 4622e0c2b58a0f3a959e9ff0354df03478c39708..e9ac026806569396a90c34e4dc634aeff8949475 100644 --- a/modules/casserver/www/validate.php +++ b/modules/casserver/www/validate.php @@ -1,96 +1,4 @@ <?php - -/* - * Incomming parameters: - * service - * renew - * ticket - * - */ - - -if (!array_key_exists('service', $_GET)) - throw new Exception('Required URL query parameter [service] not provided. (CAS Server)'); - -$service = $_GET['service']; - -if (!array_key_exists('ticket', $_GET)) - throw new Exception('Required URL query parameter [ticket] not provided. (CAS Server)'); - -$ticket = $_GET['ticket']; - -$renew = FALSE; - -if (array_key_exists('renew', $_GET)) { - $renew = TRUE; -} - - - -try { - /* Load simpleSAMLphp, configuration and metadata */ - $casconfig = SimpleSAML_Configuration::getConfig('module_casserver.php'); - - - $path = $casconfig->resolvePath($casconfig->getValue('ticketcache', 'ticketcache')); - - $ticketcontent = retrieveTicket($ticket, $path); - - $usernamefield = $casconfig->getValue('attrname', 'eduPersonPrincipalName'); - - if (array_key_exists($usernamefield, $ticketcontent)) { - returnResponse('YES', $ticketcontent[$usernamefield][0]); - } else { - returnResponse('NO'); - } - -} catch (Exception $e) { - - returnResponse('NO'); -} - -function returnResponse($value, $username = '') { - if ($value === 'YES') { - echo 'YES' . "\n" . $username; - } else { - echo 'NO' . "\n"; - } -} - - -function storeTicket($ticket, $path, &$value ) { - - if (!is_dir($path)) - throw new Exception('Directory for CAS Server ticket storage [' . $path . '] does not exists. '); - - if (!is_writable($path)) - throw new Exception('Directory for CAS Server ticket storage [' . $path . '] is not writable. '); - - $filename = $path . '/' . $ticket; - file_put_contents($filename, serialize($value)); -} - -function retrieveTicket($ticket, $path) { - - if (!preg_match('/^ST-?[a-zA-Z0-9]+$/D', $ticket)) throw new Exception('Invalid characters in ticket'); - - - if (!is_dir($path)) - throw new Exception('Directory for CAS Server ticket storage [' . $path . '] does not exists. '); - - $filename = $path . '/' . $ticket; - - if (!file_exists($filename)) - throw new Exception('Could not find ticket'); - - $content = file_get_contents($filename); - - unlink($filename); - - return unserialize($content); -} - - - - +$function = 'validate'; +include("serviceValidate.php"); ?> \ No newline at end of file