00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00037
00038 include_once(dirname(__FILE__).'/languages/languages.php');
00039
00040
00041 include_once(dirname(__FILE__).'/PGTStorage/pgt-main.php');
00042
00051 class CASClient
00052 {
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00081 function HTMLFilterOutput($str)
00082 {
00083 $str = str_replace('__CAS_VERSION__',$this->getServerVersion(),$str);
00084 $str = str_replace('__PHPCAS_VERSION__',phpCAS::getVersion(),$str);
00085 $str = str_replace('__SERVER_BASE_URL__',$this->getServerBaseURL(),$str);
00086 echo $str;
00087 }
00088
00097 var $_output_header = '';
00098
00108 function printHTMLHeader($title)
00109 {
00110 $this->HTMLFilterOutput(str_replace('__TITLE__',
00111 $title,
00112 (empty($this->_output_header)
00113 ? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>'
00114 : $this->_output_header)
00115 )
00116 );
00117 }
00118
00127 var $_output_footer = '';
00128
00136 function printHTMLFooter()
00137 {
00138 $this->HTMLFilterOutput(empty($this->_output_footer)
00139 ?('<hr><address>phpCAS __PHPCAS_VERSION__ '.$this->getString(CAS_STR_USING_SERVER).' <a href="__SERVER_BASE_URL__">__SERVER_BASE_URL__</a> (CAS __CAS_VERSION__)</a></address></body></html>')
00140 :$this->_output_footer);
00141 }
00142
00150 function setHTMLHeader($header)
00151 {
00152 $this->_output_header = $header;
00153 }
00154
00162 function setHTMLFooter($footer)
00163 {
00164 $this->_output_footer = $footer;
00165 }
00166
00168
00169
00170
00185 var $_lang = '';
00186
00194 function getLang()
00195 {
00196 if ( empty($this->_lang) )
00197 $this->setLang(PHPCAS_LANG_DEFAULT);
00198 return $this->_lang;
00199 }
00200
00210 var $_strings;
00211
00221 function getString($str)
00222 {
00223
00224 $this->getLang();
00225
00226 if ( !isset($this->_strings[$str]) ) {
00227 trigger_error('string `'.$str.'\' not defined for language `'.$this->getLang().'\'',E_USER_ERROR);
00228 }
00229 return $this->_strings[$str];
00230 }
00231
00241 function setLang($lang)
00242 {
00243
00244 include_once(dirname(__FILE__).'/languages/'.$lang.'.php');
00245
00246 if ( !is_array($this->_strings) ) {
00247 trigger_error('language `'.$lang.'\' is not implemented',E_USER_ERROR);
00248 }
00249 $this->_lang = $lang;
00250 }
00251
00253 // ########################################################################
00254 // CAS SERVER CONFIG
00255 // ########################################################################
00285 var $_server = array(
00286 'version' => -1,
00287 'hostname' => 'none',
00288 'port' => -1,
00289 'uri' => 'none'
00290 );
00291
00297 function getServerVersion()
00298 {
00299 return $this->_server['version'];
00300 }
00301
00307 function getServerHostname()
00308 { return $this->_server['hostname']; }
00309
00315 function getServerPort()
00316 { return $this->_server['port']; }
00317
00323 function getServerURI()
00324 { return $this->_server['uri']; }
00325
00331 function getServerBaseURL()
00332 {
00333 // the URL is build only when needed
00334 if ( empty($this->_server['base_url']) ) {
00335 $this->_server['base_url'] = 'https:
00336 if ($this->getServerPort()!=443) {
00337 $this->_server['base_url'] .= ':'
00338 .$this->getServerPort();
00339 }
00340 $this->_server['base_url'] .= $this->getServerURI();
00341 }
00342 return $this->_server['base_url'];
00343 }
00344
00354 function getServerLoginURL($gateway=false,$renew=false) {
00355 phpCAS::traceBegin();
00356
00357 if ( empty($this->_server['login_url']) ) {
00358 $this->_server['login_url'] = $this->getServerBaseURL();
00359 $this->_server['login_url'] .= 'login?service=';
00360
00361 $this->_server['login_url'] .= urlencode($this->getURL());
00362 if($renew) {
00363
00364 $this->_server['login_url'] .= '&renew=true';
00365 } elseif ($gateway) {
00366
00367 $this->_server['login_url'] .= '&gateway=true';
00368 }
00369 }
00370 phpCAS::traceEnd($this->_server['login_url']);
00371 return $this->_server['login_url'];
00372 }
00373
00380 function setServerLoginURL($url)
00381 {
00382 return $this->_server['login_url'] = $url;
00383 }
00384
00385
00392 function setServerServiceValidateURL($url)
00393 {
00394 return $this->_server['service_validate_url'] = $url;
00395 }
00396
00397
00404 function setServerProxyValidateURL($url)
00405 {
00406 return $this->_server['proxy_validate_url'] = $url;
00407 }
00408
00409
00416 function setServerSamlValidateURL($url)
00417 {
00418 return $this->_server['saml_validate_url'] = $url;
00419 }
00420
00421
00427 function getServerServiceValidateURL()
00428 {
00429
00430 if ( empty($this->_server['service_validate_url']) ) {
00431 switch ($this->getServerVersion()) {
00432 case CAS_VERSION_1_0:
00433 $this->_server['service_validate_url'] = $this->getServerBaseURL().'validate';
00434 break;
00435 case CAS_VERSION_2_0:
00436 $this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate';
00437 break;
00438 }
00439 }
00440
00441 return $this->_server['service_validate_url'].'?service='.urlencode($this->getURL());
00442 }
00448 function getServerSamlValidateURL()
00449 {
00450 phpCAS::traceBegin();
00451
00452 if ( empty($this->_server['saml_validate_url']) ) {
00453 switch ($this->getServerVersion()) {
00454 case SAML_VERSION_1_1:
00455 $this->_server['saml_validate_url'] = $this->getServerBaseURL().'samlValidate';
00456 break;
00457 }
00458 }
00459 phpCAS::traceEnd($this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL()));
00460 return $this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL());
00461 }
00467 function getServerProxyValidateURL()
00468 {
00469
00470 if ( empty($this->_server['proxy_validate_url']) ) {
00471 switch ($this->getServerVersion()) {
00472 case CAS_VERSION_1_0:
00473 $this->_server['proxy_validate_url'] = '';
00474 break;
00475 case CAS_VERSION_2_0:
00476 $this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate';
00477 break;
00478 }
00479 }
00480
00481 return $this->_server['proxy_validate_url'].'?service='.urlencode($this->getURL());
00482 }
00483
00489 function getServerProxyURL()
00490 {
00491
00492 if ( empty($this->_server['proxy_url']) ) {
00493 switch ($this->getServerVersion()) {
00494 case CAS_VERSION_1_0:
00495 $this->_server['proxy_url'] = '';
00496 break;
00497 case CAS_VERSION_2_0:
00498 $this->_server['proxy_url'] = $this->getServerBaseURL().'proxy';
00499 break;
00500 }
00501 }
00502 return $this->_server['proxy_url'];
00503 }
00504
00510 function getServerLogoutURL()
00511 {
00512
00513 if ( empty($this->_server['logout_url']) ) {
00514 $this->_server['logout_url'] = $this->getServerBaseURL().'logout';
00515 }
00516 return $this->_server['logout_url'];
00517 }
00518
00525 function setServerLogoutURL($url)
00526 {
00527 return $this->_server['logout_url'] = $url;
00528 }
00529
00533 var $_curl_options = array();
00534
00538 function setExtraCurlOption($key, $value)
00539 {
00540 $this->_curl_options[$key] = $value;
00541 }
00542
00548 function isHttps() {
00549
00550
00551 if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
00552 return true;
00553 } else {
00554 return false;
00555 }
00556 }
00557
00558
00559
00560
00575 function CASClient(
00576 $server_version,
00577 $proxy,
00578 $server_hostname,
00579 $server_port,
00580 $server_uri,
00581 $start_session = true) {
00582
00583 phpCAS::traceBegin();
00584
00585
00586 if (version_compare(PHP_VERSION,'5','>=') && ini_get('zend.ze1_compatibility_mode')) {
00587 phpCAS::error('phpCAS cannot support zend.ze1_compatibility_mode. Sorry.');
00588 }
00589 $this->_start_session = $start_session;
00590
00591 if ($this->_start_session && session_id() !== "")
00592 {
00593 phpCAS :: error("Another session was started before phpcas. Either disable the session" .
00594 " handling for phpcas in the client() call or modify your application to leave" .
00595 " session handling to phpcas");
00596 }
00597
00598 if ($start_session && !$this->isLogoutRequest())
00599 {
00600 phpCAS :: trace("Starting a new session");
00601 session_start();
00602 }
00603
00604
00605
00606 $this->_proxy = $proxy;
00607
00608
00609 switch ($server_version) {
00610 case CAS_VERSION_1_0:
00611 if ( $this->isProxy() )
00612 phpCAS::error('CAS proxies are not supported in CAS '
00613 .$server_version);
00614 break;
00615 case CAS_VERSION_2_0:
00616 break;
00617 case SAML_VERSION_1_1:
00618 break;
00619 default:
00620 phpCAS::error('this version of CAS (`'
00621 .$server_version
00622 .'\') is not supported by phpCAS '
00623 .phpCAS::getVersion());
00624 }
00625 $this->_server['version'] = $server_version;
00626
00627 // check hostname
00628 if ( empty($server_hostname)
00629 || !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) {
00630 phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')');
00631 }
00632 $this->_server['hostname'] = $server_hostname;
00633
00634
00635 if ( $server_port == 0
00636 || !is_int($server_port) ) {
00637 phpCAS::error('bad CAS server port (`'.$server_hostname.'\')');
00638 }
00639 $this->_server['port'] = $server_port;
00640
00641 // check URI
00642 if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) {
00643 phpCAS::error('bad CAS server URI (`'.$server_uri.'\')');
00644 }
00645
00646 $server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
00647 $this->_server['uri'] = $server_uri;
00648
00649
00650 if ( $this->isProxy() ) {
00651 $this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
00652 }
00653
00654 if ( $this->isCallbackMode() ) {
00655
00656 if ( !$this->isHttps() ) {
00657 phpCAS::error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server');
00658 }
00659 } else {
00660
00661 $ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null);
00662 switch ($this->getServerVersion()) {
00663 case CAS_VERSION_1_0:
00664 if( preg_match('/^ST-/',$ticket) ) {
00665 phpCAS::trace('ST \''.$ticket.'\' found');
00666 //ST present
00667 $this->setST($ticket);
00668 //ticket has been taken into account, unset it to hide it to applications
00669 unset($_GET['ticket']);
00670 } else if ( !empty($ticket) ) {
00671 //ill-formed ticket, halt
00672 phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
00673 }
00674 break;
00675 case CAS_VERSION_2_0:
00676 if( preg_match('/^[SP]T-/',$ticket) ) {
00677 phpCAS::trace('ST or PT \''.$ticket.'\' found');
00678 $this->setPT($ticket);
00679 unset($_GET['ticket']);
00680 } else if ( !empty($ticket) ) {
00681 //ill-formed ticket, halt
00682 phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
00683 }
00684 break;
00685 case SAML_VERSION_1_1:
00686 if( preg_match('/^[SP]T-/',$ticket) ) {
00687 phpCAS::trace('SA \''.$ticket.'\' found');
00688 $this->setSA($ticket);
00689 unset($_GET['ticket']);
00690 } else if ( !empty($ticket) ) {
00691 //ill-formed ticket, halt
00692 phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
00693 }
00694 break;
00695 }
00696 }
00697 phpCAS::traceEnd();
00698 }
00699
00702
00703
00704
00705
00706
00707
00713 var $_start_session = true;
00714
00715 function setStartSession($session)
00716 {
00717 $this->_start_session = session;
00718 }
00719
00720 function getStartSession($session)
00721 {
00722 $this->_start_session = session;
00723 }
00724
00728 function renameSession($ticket)
00729 {
00730 phpCAS::traceBegin();
00731 if($this->_start_session){
00732 if (!empty ($this->_user))
00733 {
00734 $old_session = $_SESSION;
00735 session_destroy();
00736
00737 $session_id = preg_replace('/[^\w]/', '', $ticket);
00738 phpCAS :: trace("Session ID: ".$session_id);
00739 session_id($session_id);
00740 session_start();
00741 phpCAS :: trace("Restoring old session vars");
00742 $_SESSION = $old_session;
00743 } else
00744 {
00745 phpCAS :: error('Session should only be renamed after successfull authentication');
00746 }
00747 }else{
00748 phpCAS :: trace("Skipping session rename since phpCAS is not handling the session.");
00749 }
00750 phpCAS::traceEnd();
00751 }
00752
00753
00754
00755
00756
00757
00758
00771 var $_user = '';
00772
00780 function setUser($user)
00781 {
00782 $this->_user = $user;
00783 }
00784
00792 function getUser()
00793 {
00794 if ( empty($this->_user) ) {
00795 phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
00796 }
00797 return $this->_user;
00798 }
00799
00800
00801
00802
00803
00804
00805
00806
00807
00815 var $_attributes = array();
00816
00817 function setAttributes($attributes)
00818 { $this->_attributes = $attributes; }
00819
00820 function getAttributes() {
00821 if ( empty($this->_user) ) {
00822 phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
00823 }
00824 return $this->_attributes;
00825 }
00826
00827 function hasAttributes()
00828 { return !empty($this->_attributes); }
00829
00830 function hasAttribute($key)
00831 { return (is_array($this->_attributes) && array_key_exists($key, $this->_attributes)); }
00832
00833 function getAttribute($key) {
00834 if($this->hasAttribute($key)) {
00835 return $this->_attributes[$key];
00836 }
00837 }
00838
00845 function renewAuthentication(){
00846 phpCAS::traceBegin();
00847
00848 if( isset( $_SESSION['phpCAS']['auth_checked'] ) )
00849 unset($_SESSION['phpCAS']['auth_checked']);
00850 if ( $this->isAuthenticated() ) {
00851 phpCAS::trace('user already authenticated; renew');
00852 $this->redirectToCas(false,true);
00853 } else {
00854 $this->redirectToCas();
00855 }
00856 phpCAS::traceEnd();
00857 }
00858
00865 function forceAuthentication()
00866 {
00867 phpCAS::traceBegin();
00868
00869 if ( $this->isAuthenticated() ) {
00870
00871 phpCAS::trace('no need to authenticate');
00872 $res = TRUE;
00873 } else {
00874
00875 if (isset($_SESSION['phpCAS']['auth_checked'])) {
00876 unset($_SESSION['phpCAS']['auth_checked']);
00877 }
00878 $this->redirectToCas(FALSE);
00879
00880 $res = FALSE;
00881 }
00882 phpCAS::traceEnd($res);
00883 return $res;
00884 }
00885
00892 var $_cache_times_for_auth_recheck = 0;
00893
00901 function setCacheTimesForAuthRecheck($n)
00902 {
00903 $this->_cache_times_for_auth_recheck = $n;
00904 }
00905
00911 function checkAuthentication()
00912 {
00913 phpCAS::traceBegin();
00914
00915 if ( $this->isAuthenticated() ) {
00916 phpCAS::trace('user is authenticated');
00917 $res = TRUE;
00918 } else if (isset($_SESSION['phpCAS']['auth_checked'])) {
00919
00920 unset($_SESSION['phpCAS']['auth_checked']);
00921 $res = FALSE;
00922 } else {
00923
00924
00925
00926
00927
00928 if (! isset($_SESSION['phpCAS']['unauth_count']) )
00929 $_SESSION['phpCAS']['unauth_count'] = -2;
00930
00931 if (($_SESSION['phpCAS']['unauth_count'] != -2 && $this->_cache_times_for_auth_recheck == -1)
00932 || ($_SESSION['phpCAS']['unauth_count'] >= 0 && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck))
00933 {
00934 $res = FALSE;
00935
00936 if ($this->_cache_times_for_auth_recheck != -1)
00937 {
00938 $_SESSION['phpCAS']['unauth_count']++;
00939 phpCAS::trace('user is not authenticated (cached for '.$_SESSION['phpCAS']['unauth_count'].' times of '.$this->_cache_times_for_auth_recheck.')');
00940 }
00941 else
00942 {
00943 phpCAS::trace('user is not authenticated (cached for until login pressed)');
00944 }
00945 }
00946 else
00947 {
00948 $_SESSION['phpCAS']['unauth_count'] = 0;
00949 $_SESSION['phpCAS']['auth_checked'] = true;
00950 phpCAS::trace('user is not authenticated (cache reset)');
00951 $this->redirectToCas(TRUE);
00952
00953 $res = FALSE;
00954 }
00955 }
00956 phpCAS::traceEnd($res);
00957 return $res;
00958 }
00959
00968 function isAuthenticated()
00969 {
00970 phpCAS::traceBegin();
00971 $res = FALSE;
00972 $validate_url = '';
00973
00974 if ( $this->wasPreviouslyAuthenticated() ) {
00975 if($this->hasST() || $this->hasPT() || $this->hasSA()){
00976
00977 phpCAS::trace('ticket was present and will be discarded, use renewAuthenticate()');
00978 header('Location: '.$this->getURL());
00979 phpCAS::log( "Prepare redirect to remove ticket: ".$this->getURL() );
00980 phpCAS::traceExit();
00981 exit();
00982 }else{
00983
00984
00985 phpCAS::trace('user was already authenticated, no need to look for tickets');
00986 $res = TRUE;
00987 }
00988 }
00989 else {
00990 if ( $this->hasST() ) {
00991
00992 phpCAS::trace('ST `'.$this->getST().'\' is present');
00993 $this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
00994 phpCAS::trace('ST `'.$this->getST().'\' was validated');
00995 if ( $this->isProxy() ) {
00996 $this->validatePGT($validate_url,$text_response,$tree_response);
00997 phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
00998 $_SESSION['phpCAS']['pgt'] = $this->getPGT();
00999 }
01000 $_SESSION['phpCAS']['user'] = $this->getUser();
01001 $res = TRUE;
01002 }
01003 elseif ( $this->hasPT() ) {
01004 // if a Proxy Ticket was given, validate it
01005 phpCAS::trace('PT `'.$this->getPT().'\' is present');
01006 $this->validatePT($validate_url,$text_response,$tree_response);
01007 phpCAS::trace('PT `'.$this->getPT().'\' was validated');
01008 if ( $this->isProxy() ) {
01009 $this->validatePGT($validate_url,$text_response,$tree_response); // idem
01010 phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
01011 $_SESSION['phpCAS']['pgt'] = $this->getPGT();
01012 }
01013 $_SESSION['phpCAS']['user'] = $this->getUser();
01014 $res = TRUE;
01015 }
01016 elseif ( $this->hasSA() ) {
01017
01018 phpCAS::trace('SA `'.$this->getSA().'\' is present');
01019 $this->validateSA($validate_url,$text_response,$tree_response); // if it fails, it halts
01020 phpCAS::trace('SA `'.$this->getSA().'\' was validated');
01021 $_SESSION['phpCAS']['user'] = $this->getUser();
01022 $_SESSION['phpCAS']['attributes'] = $this->getAttributes();
01023 $res = TRUE;
01024 }
01025 else {
01026
01027 phpCAS::trace('no ticket found');
01028 }
01029 if ($res) {
01030
01031
01032
01033 header('Location: '.$this->getURL());
01034 phpCAS::log( "Prepare redirect to : ".$this->getURL() );
01035 phpCAS::traceExit();
01036 exit();
01037 }
01038 }
01039
01040 phpCAS::traceEnd($res);
01041 return $res;
01042 }
01043
01049 function isSessionAuthenticated ()
01050 {
01051 return !empty($_SESSION['phpCAS']['user']);
01052 }
01053
01064 function wasPreviouslyAuthenticated()
01065 {
01066 phpCAS::traceBegin();
01067
01068 if ( $this->isCallbackMode() ) {
01069 $this->callback();
01070 }
01071
01072 $auth = FALSE;
01073
01074 if ( $this->isProxy() ) {
01075
01076 if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
01077
01078 $this->setUser($_SESSION['phpCAS']['user']);
01079 $this->setPGT($_SESSION['phpCAS']['pgt']);
01080 phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\'');
01081 $auth = TRUE;
01082 } elseif ( $this->isSessionAuthenticated() && empty($_SESSION['phpCAS']['pgt']) ) {
01083
01084 phpCAS::trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
01085 // unset all tickets to enforce authentication
01086 unset($_SESSION['phpCAS']);
01087 $this->setST('');
01088 $this->setPT('');
01089 } elseif ( !$this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
01090 // these two variables should be empty or not empty at the same time
01091 phpCAS::trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty');
01092
01093 unset($_SESSION['phpCAS']);
01094 $this->setST('');
01095 $this->setPT('');
01096 } else {
01097 phpCAS::trace('neither user not PGT found');
01098 }
01099 } else {
01100
01101 if ( $this->isSessionAuthenticated() ) {
01102
01103 $this->setUser($_SESSION['phpCAS']['user']);
01104 if(isset($_SESSION['phpCAS']['attributes'])){
01105 $this->setAttributes($_SESSION['phpCAS']['attributes']);
01106 }
01107 phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\'');
01108 $auth = TRUE;
01109 } else {
01110 phpCAS::trace('no user found');
01111 }
01112 }
01113
01114 phpCAS::traceEnd($auth);
01115 return $auth;
01116 }
01117
01125 function redirectToCas($gateway=false,$renew=false){
01126 phpCAS::traceBegin();
01127 $cas_url = $this->getServerLoginURL($gateway,$renew);
01128 header('Location: '.$cas_url);
01129 phpCAS::log( "Redirect to : ".$cas_url );
01130
01131 $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
01132
01133 printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
01134 $this->printHTMLFooter();
01135
01136 phpCAS::traceExit();
01137 exit();
01138 }
01139
01140
01146 function logout($params) {
01147 phpCAS::traceBegin();
01148 $cas_url = $this->getServerLogoutURL();
01149 $paramSeparator = '?';
01150 if (isset($params['url'])) {
01151 $cas_url = $cas_url . $paramSeparator . "url=" . urlencode($params['url']);
01152 $paramSeparator = '&';
01153 }
01154 if (isset($params['service'])) {
01155 $cas_url = $cas_url . $paramSeparator . "service=" . urlencode($params['service']);
01156 }
01157 header('Location: '.$cas_url);
01158 phpCAS::log( "Prepare redirect to : ".$cas_url );
01159
01160 session_unset();
01161 session_destroy();
01162
01163 $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
01164 printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
01165 $this->printHTMLFooter();
01166
01167 phpCAS::traceExit();
01168 exit();
01169 }
01170
01175 function isLogoutRequest() {
01176 return !empty($_POST['logoutRequest']);
01177 }
01178
01183 function isLogoutRequestAllowed() {
01184 }
01185
01194 function handleLogoutRequests($check_client=true, $allowed_clients=false) {
01195 phpCAS::traceBegin();
01196 if (!$this->isLogoutRequest()) {
01197 phpCAS::log("Not a logout request");
01198 phpCAS::traceEnd();
01199 return;
01200 }
01201 if(!$this->_start_session){
01202 phpCAS::log("phpCAS can't handle logout requests if it does not manage the session.");
01203 }
01204 phpCAS::log("Logout requested");
01205 phpCAS::log("SAML REQUEST: ".$_POST['logoutRequest']);
01206 if ($check_client) {
01207 if (!$allowed_clients) {
01208 $allowed_clients = array( $this->getServerHostname() );
01209 }
01210 $client_ip = $_SERVER['REMOTE_ADDR'];
01211 $client = gethostbyaddr($client_ip);
01212 phpCAS::log("Client: ".$client."/".$client_ip);
01213 $allowed = false;
01214 foreach ($allowed_clients as $allowed_client) {
01215 if (($client == $allowed_client) or ($client_ip == $allowed_client)) {
01216 phpCAS::log("Allowed client '".$allowed_client."' matches, logout request is allowed");
01217 $allowed = true;
01218 break;
01219 } else {
01220 phpCAS::log("Allowed client '".$allowed_client."' does not match");
01221 }
01222 }
01223 if (!$allowed) {
01224 phpCAS::error("Unauthorized logout request from client '".$client."'");
01225 printf("Unauthorized!");
01226 phpCAS::traceExit();
01227 exit();
01228 }
01229 } else {
01230 phpCAS::log("No access control set");
01231 }
01232
01233 preg_match("|<samlp:SessionIndex>(.*)</samlp:SessionIndex>|", $_POST['logoutRequest'], $tick, PREG_OFFSET_CAPTURE, 3);
01234 $wrappedSamlSessionIndex = preg_replace('|<samlp:SessionIndex>|','',$tick[0][0]);
01235 $ticket2logout = preg_replace('|</samlp:SessionIndex>|','',$wrappedSamlSessionIndex);
01236 phpCAS::log("Ticket to logout: ".$ticket2logout);
01237 $session_id = preg_replace('/[^\w]/','',$ticket2logout);
01238 phpCAS::log("Session id: ".$session_id);
01239
01240
01241 if(session_id() !== ""){
01242 session_unset();
01243 session_destroy();
01244 }
01245
01246 session_id($session_id);
01247 $_COOKIE[session_name()]=$session_id;
01248 $_GET[session_name()]=$session_id;
01249
01250
01251 session_start();
01252 session_unset();
01253 session_destroy();
01254 printf("Disconnected!");
01255 phpCAS::traceExit();
01256 exit();
01257 }
01258
01261
01262
01263
01264
01265
01266
01267
01268
01269
01283 var $_st = '';
01284
01290 function getST()
01291 { return $this->_st; }
01292
01298 function setST($st)
01299 { $this->_st = $st; }
01300
01306 function hasST()
01307 { return !empty($this->_st); }
01308
01311
01312
01313
01325 var $_cas_server_cert = '';
01326
01333 var $_cas_server_ca_cert = '';
01334
01341 var $_no_cas_server_validation = false;
01342
01348 function setCasServerCert($cert)
01349 {
01350 $this->_cas_server_cert = $cert;
01351 }
01352
01358 function setCasServerCACert($cert)
01359 {
01360 $this->_cas_server_ca_cert = $cert;
01361 }
01362
01366 function setNoCasServerValidation()
01367 {
01368 $this->_no_cas_server_validation = true;
01369 }
01370
01384 function validateST($validate_url,&$text_response,&$tree_response)
01385 {
01386 phpCAS::traceBegin();
01387
01388 $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
01389 if ( $this->isProxy() ) {
01390
01391 $validate_url .= '&pgtUrl='.urlencode($this->getCallbackURL());
01392 }
01393
01394
01395 if ( !$this->readURL($validate_url,'',$headers,$text_response,$err_msg) ) {
01396 phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
01397 $this->authError('ST not validated',
01398 $validate_url,
01399 TRUE/*$no_response*/);
01400 }
01401
01402 // analyze the result depending on the version
01403 switch ($this->getServerVersion()) {
01404 case CAS_VERSION_1_0:
01405 if (preg_match('/^no\n/',$text_response)) {
01406 phpCAS::trace('ST has not been validated');
01407 $this->authError('ST not validated',
01408 $validate_url,
01409 FALSE/*$no_response*/,
01410 FALSE/*$bad_response*/,
01411 $text_response);
01412 }
01413 if (!preg_match('/^yes\n/',$text_response)) {
01414 phpCAS::trace('ill-formed response');
01415 $this->authError('ST not validated',
01416 $validate_url,
01417 FALSE/*$no_response*/,
01418 TRUE/*$bad_response*/,
01419 $text_response);
01420 }
01421 // ST has been validated, extract the user name
01422 $arr = preg_split('/\n/',$text_response);
01423 $this->setUser(trim($arr[1]));
01424 break;
01425 case CAS_VERSION_2_0:
01426 // read the response of the CAS server into a DOM object
01427 if ( !($dom = domxml_open_mem($text_response))) {
01428 phpCAS::trace('domxml_open_mem() failed');
01429 $this->authError('ST not validated',
01430 $validate_url,
01431 FALSE/*$no_response*/,
01432 TRUE/*$bad_response*/,
01433 $text_response);
01434 }
01435 // read the root node of the XML tree
01436 if ( !($tree_response = $dom->document_element()) ) {
01437 phpCAS::trace('document_element() failed');
01438 $this->authError('ST not validated',
01439 $validate_url,
01440 FALSE/*$no_response*/,
01441 TRUE/*$bad_response*/,
01442 $text_response);
01443 }
01444 // insure that tag name is 'serviceResponse'
01445 if ( $tree_response->node_name() != 'serviceResponse' ) {
01446 phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name().'\'');
01447 $this->authError('ST not validated',
01448 $validate_url,
01449 FALSE,
01450 TRUE,
01451 $text_response);
01452 }
01453 if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
01454
01455 if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) {
01456 phpCAS::trace('<authenticationSuccess> found, but no <user>');
01457 $this->authError('ST not validated',
01458 $validate_url,
01459 FALSE,
01460 TRUE,
01461 $text_response);
01462 }
01463 $user = trim($user_elements[0]->get_content());
01464 phpCAS::trace('user = `'.$user);
01465 $this->setUser($user);
01466
01467 } else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
01468 phpCAS::trace('<authenticationFailure> found');
01469
01470 $this->authError('ST not validated',
01471 $validate_url,
01472 FALSE,
01473 FALSE,
01474 $text_response,
01475 $failure_elements[0]->get_attribute('code'),
01476 trim($failure_elements[0]->get_content()));
01477 } else {
01478 phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
01479 $this->authError('ST not validated',
01480 $validate_url,
01481 FALSE,
01482 TRUE,
01483 $text_response);
01484 }
01485 break;
01486 }
01487 $this->renameSession($this->getST());
01488
01489 phpCAS::traceEnd(TRUE);
01490 return TRUE;
01491 }
01492
01493
01494
01495
01514 function validateSA($validate_url,&$text_response,&$tree_response)
01515 {
01516 phpCAS::traceBegin();
01517
01518
01519 $validate_url = $this->getServerSamlValidateURL();
01520
01521
01522 if ( !$this->readURL($validate_url,'',$headers,$text_response,$err_msg) ) {
01523 phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
01524 $this->authError('SA not validated', $validate_url, TRUE/*$no_response*/);
01525 }
01526
01527 phpCAS::trace('server version: '.$this->getServerVersion());
01528
01529 // analyze the result depending on the version
01530 switch ($this->getServerVersion()) {
01531 case SAML_VERSION_1_1:
01532
01533 // read the response of the CAS server into a DOM object
01534 if ( !($dom = domxml_open_mem($text_response))) {
01535 phpCAS::trace('domxml_open_mem() failed');
01536 $this->authError('SA not validated',
01537 $validate_url,
01538 FALSE/*$no_response*/,
01539 TRUE/*$bad_response*/,
01540 $text_response);
01541 }
01542 // read the root node of the XML tree
01543 if ( !($tree_response = $dom->document_element()) ) {
01544 phpCAS::trace('document_element() failed');
01545 $this->authError('SA not validated',
01546 $validate_url,
01547 FALSE/*$no_response*/,
01548 TRUE/*$bad_response*/,
01549 $text_response);
01550 }
01551 // insure that tag name is 'Envelope'
01552 if ( $tree_response->node_name() != 'Envelope' ) {
01553 phpCAS::trace('bad XML root node (should be `Envelope\' instead of `'.$tree_response->node_name().'\'');
01554 $this->authError('SA not validated',
01555 $validate_url,
01556 FALSE,
01557 TRUE,
01558 $text_response);
01559 }
01560
01561 if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("NameIdentifier")) != 0) {
01562 phpCAS::trace('NameIdentifier found');
01563 $user = trim($success_elements[0]->get_content());
01564 phpCAS::trace('user = `'.$user.'`');
01565 $this->setUser($user);
01566 $this->setSessionAttributes($text_response);
01567 } else {
01568 phpCAS::trace('no <NameIdentifier> tag found in SAML payload');
01569 $this->authError('SA not validated',
01570 $validate_url,
01571 FALSE,
01572 TRUE,
01573 $text_response);
01574 }
01575 break;
01576 }
01577 $this->renameSession($this->getSA());
01578
01579 phpCAS::traceEnd(TRUE);
01580 return TRUE;
01581 }
01582
01592 function setSessionAttributes($text_response)
01593 {
01594 phpCAS::traceBegin();
01595
01596 $result = FALSE;
01597
01598 if (isset($_SESSION[SAML_ATTRIBUTES])) {
01599 phpCAS::trace("session attrs already set.");
01600 }
01601
01602 $attr_array = array();
01603
01604 if (($dom = domxml_open_mem($text_response))) {
01605 $xPath = $dom->xpath_new_context();
01606 $xPath->xpath_register_ns('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol');
01607 $xPath->xpath_register_ns('saml', 'urn:oasis:names:tc:SAML:1.0:assertion');
01608 $nodelist = $xPath->xpath_eval("//saml:Attribute");
01609 if($nodelist){
01610 $attrs = $nodelist->nodeset;
01611 foreach($attrs as $attr){
01612 $xres = $xPath->xpath_eval("saml:AttributeValue", $attr);
01613 $name = $attr->get_attribute("AttributeName");
01614 $value_array = array();
01615 foreach($xres->nodeset as $node){
01616 $value_array[] = $node->get_content();
01617 }
01618 $attr_array[$name] = $value_array;
01619 }
01620 $_SESSION[SAML_ATTRIBUTES] = $attr_array;
01621
01622 foreach($attr_array as $attr_key => $attr_value) {
01623 if(count($attr_value) > 1) {
01624 $this->_attributes[$attr_key] = $attr_value;
01625 phpCAS::trace("* " . $attr_key . "=" . $attr_value);
01626 }
01627 else {
01628 $this->_attributes[$attr_key] = $attr_value[0];
01629 phpCAS::trace("* " . $attr_key . "=" . $attr_value[0]);
01630 }
01631 }
01632 $result = TRUE;
01633 }else{
01634 phpCAS::trace("SAML Attributes are empty");
01635 $result = FALSE;
01636 }
01637 }
01638 phpCAS::traceEnd($result);
01639 return $result;
01640 }
01641
01644
01645
01646
01647
01648
01649
01650
01651
01652
01664 var $_proxy;
01665
01673 function isProxy()
01674 {
01675 return $this->_proxy;
01676 }
01677
01679
01680
01681
01694 var $_pgt = '';
01695
01701 function getPGT()
01702 { return $this->_pgt; }
01703
01709 function setPGT($pgt)
01710 { $this->_pgt = $pgt; }
01711
01717 function hasPGT()
01718 { return !empty($this->_pgt); }
01719
01722
01723
01724
01742 var $_callback_mode = FALSE;
01743
01751 function setCallbackMode($callback_mode)
01752 {
01753 $this->_callback_mode = $callback_mode;
01754 }
01755
01764 function isCallbackMode()
01765 {
01766 return $this->_callback_mode;
01767 }
01768
01777 var $_callback_url = '';
01778
01788 function getCallbackURL()
01789 {
01790
01791 if ( empty($this->_callback_url) ) {
01792 $final_uri = '';
01793
01794 $final_uri = 'https://';
01795
01796
01797
01798 if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
01799
01800
01801
01802 if (empty($_SERVER['SERVER_NAME'])) {
01803 $final_uri .= $_SERVER['HTTP_HOST'];
01804 } else {
01805 $final_uri .= $_SERVER['SERVER_NAME'];
01806 }
01807 } else {
01808 $final_uri .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
01809 }
01810 if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
01811 || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
01812 $final_uri .= ':';
01813 $final_uri .= $_SERVER['SERVER_PORT'];
01814 }
01815 $request_uri = $_SERVER['REQUEST_URI'];
01816 $request_uri = preg_replace('/\?.*$/','',$request_uri);
01817 $final_uri .= $request_uri;
01818 $this->setCallbackURL($final_uri);
01819 }
01820 return $this->_callback_url;
01821 }
01822
01830 function setCallbackURL($url)
01831 {
01832 return $this->_callback_url = $url;
01833 }
01834
01841 function callback()
01842 {
01843 phpCAS::traceBegin();
01844 if (preg_match('/PGTIOU-[\.\-\w]/', $_GET['pgtIou'])){
01845 if(preg_match('/[PT]GT-[\.\-\w]/', $_GET['pgtId'])){
01846 $this->printHTMLHeader('phpCAS callback');
01847 $pgt_iou = $_GET['pgtIou'];
01848 $pgt = $_GET['pgtId'];
01849 phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')');
01850 echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>';
01851 $this->storePGT($pgt,$pgt_iou);
01852 $this->printHTMLFooter();
01853 }else{
01854 phpCAS::error('PGT format invalid' . $_GET['pgtId']);
01855 }
01856 }else{
01857 phpCAS::error('PGTiou format invalid' . $_GET['pgtIou']);
01858 }
01859 phpCAS::traceExit();
01860 exit();
01861 }
01862
01865
01866
01867
01881 var $_pgt_storage = null;
01882
01889 function initPGTStorage()
01890 {
01891
01892 if ( !is_object($this->_pgt_storage) ) {
01893 $this->setPGTStorageFile();
01894 }
01895
01896
01897 $this->_pgt_storage->init();
01898 }
01899
01908 function storePGT($pgt,$pgt_iou)
01909 {
01910
01911 $this->initPGTStorage();
01912
01913 $this->_pgt_storage->write($pgt,$pgt_iou);
01914 }
01915
01925 function loadPGT($pgt_iou)
01926 {
01927
01928 $this->initPGTStorage();
01929
01930 return $this->_pgt_storage->read($pgt_iou);
01931 }
01932
01942 function setPGTStorageFile($format='',
01943 $path='')
01944 {
01945
01946 if ( is_object($this->_pgt_storage) ) {
01947 phpCAS::error('PGT storage already defined');
01948 }
01949
01950
01951 $this->_pgt_storage = new PGTStorageFile($this,$format,$path);
01952 }
01953
01954
01955
01956
01970 function validatePGT(&$validate_url,$text_response,$tree_response)
01971 {
01972
01973 phpCAS::log('start validatePGT()');
01974 if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) {
01975 phpCAS::trace('<proxyGrantingTicket> not found');
01976
01977 $this->authError('Ticket validated but no PGT Iou transmitted',
01978 $validate_url,
01979 FALSE,
01980 FALSE,
01981 $text_response);
01982 } else {
01983
01984 $pgt_iou = trim($arr[0]->get_content());
01985 if(preg_match('/PGTIOU-[\.\-\w]/',$pgt_iou)){
01986 $pgt = $this->loadPGT($pgt_iou);
01987 if ( $pgt == FALSE ) {
01988 phpCAS::trace('could not load PGT');
01989 $this->authError('PGT Iou was transmitted but PGT could not be retrieved',
01990 $validate_url,
01991 FALSE,
01992 FALSE,
01993 $text_response);
01994 }
01995 $this->setPGT($pgt);
01996 }else{
01997 phpCAS::trace('PGTiou format error');
01998 $this->authError('PGT Iou was transmitted but has wrong fromat',
01999 $validate_url,
02000 FALSE,
02001 FALSE,
02002 $text_response);
02003 }
02004
02005 }
02006
02007 phpCAS::log('end validatePGT()');
02008 return TRUE;
02009 }
02010
02011
02012
02013
02014
02026 function retrievePT($target_service,&$err_code,&$err_msg)
02027 {
02028 phpCAS::traceBegin();
02029
02030
02031
02032
02033
02034 $err_msg = '';
02035
02036
02037
02038 $cas_url = $this->getServerProxyURL().'?targetService='.urlencode($target_service).'&pgt='.$this->getPGT();
02039
02040
02041 if ( !$this->readURL($cas_url,'',$headers,$cas_response,$err_msg) ) {
02042 phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')');
02043 $err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
02044 $err_msg = 'could not retrieve PT (no response from the CAS server)';
02045 phpCAS::traceEnd(FALSE);
02046 return FALSE;
02047 }
02048
02049 $bad_response = FALSE;
02050
02051 if ( !$bad_response ) {
02052 // read the response of the CAS server into a DOM object
02053 if ( !($dom = @domxml_open_mem($cas_response))) {
02054 phpCAS::trace('domxml_open_mem() failed');
02055 // read failed
02056 $bad_response = TRUE;
02057 }
02058 }
02059
02060 if ( !$bad_response ) {
02061 // read the root node of the XML tree
02062 if ( !($root = $dom->document_element()) ) {
02063 phpCAS::trace('document_element() failed');
02064 // read failed
02065 $bad_response = TRUE;
02066 }
02067 }
02068
02069 if ( !$bad_response ) {
02070 // insure that tag name is 'serviceResponse'
02071 if ( $root->node_name() != 'serviceResponse' ) {
02072 phpCAS::trace('node_name() failed');
02073 // bad root node
02074 $bad_response = TRUE;
02075 }
02076 }
02077
02078 if ( !$bad_response ) {
02079 // look for a proxySuccess tag
02080 if ( sizeof($arr = $root->get_elements_by_tagname("proxySuccess")) != 0) {
02081 // authentication succeded, look for a proxyTicket tag
02082 if ( sizeof($arr = $root->get_elements_by_tagname("proxyTicket")) != 0) {
02083 $err_code = PHPCAS_SERVICE_OK;
02084 $err_msg = '';
02085 phpCAS::trace('original PT: '.trim($arr[0]->get_content()));
02086 $pt = trim($arr[0]->get_content());
02087 phpCAS::traceEnd($pt);
02088 return $pt;
02089 } else {
02090 phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
02091 }
02092 }
02093 // look for a proxyFailure tag
02094 else if ( sizeof($arr = $root->get_elements_by_tagname("proxyFailure")) != 0) {
02095 // authentication failed, extract the error
02096 $err_code = PHPCAS_SERVICE_PT_FAILURE;
02097 $err_msg = 'PT retrieving failed (code=`'
02098 .$arr[0]->get_attribute('code')
02099 .'\', message=`'
02100 .trim($arr[0]->get_content())
02101 .'\')';
02102 phpCAS::traceEnd(FALSE);
02103 return FALSE;
02104 } else {
02105 phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
02106 }
02107 }
02108
02109 // at this step, we are sure that the response of the CAS server was ill-formed
02110 $err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
02111 $err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')';
02112
02113 phpCAS::traceEnd(FALSE);
02114 return FALSE;
02115 }
02116
02117
02118
02119
02120
02136 function readURL($url,$cookies,&$headers,&$body,&$err_msg)
02137 {
02138 phpCAS::traceBegin();
02139 $headers = '';
02140 $body = '';
02141 $err_msg = '';
02142
02143 $res = TRUE;
02144
02145
02146 $ch = curl_init($url);
02147
02148 if (version_compare(PHP_VERSION,'5.1.3','>=')) {
02149
02150 curl_setopt_array($ch, $this->_curl_options);
02151 } else {
02152 foreach ($this->_curl_options as $key => $value) {
02153 curl_setopt($ch, $key, $value);
02154 }
02155 }
02156
02157 if ($this->_cas_server_cert == '' && $this->_cas_server_ca_cert == '' && !$this->_no_cas_server_validation) {
02158 phpCAS::error('one of the methods phpCAS::setCasServerCert(), phpCAS::setCasServerCACert() or phpCAS::setNoCasServerValidation() must be called.');
02159 }
02160 if ($this->_cas_server_cert != '' && $this->_cas_server_ca_cert != '') {
02161
02162 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
02163 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
02164 curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
02165 curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
02166 curl_setopt($ch, CURLOPT_VERBOSE, '1');
02167 phpCAS::trace('CURL: Set all required opts for mutual authentication ------');
02168 } else if ($this->_cas_server_cert != '' ) {
02169 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
02170 curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
02171 } else if ($this->_cas_server_ca_cert != '') {
02172 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
02173 curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
02174 } else {
02175 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
02176 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
02177 }
02178
02179
02180 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
02181
02182 $this->_curl_headers = array();
02183 curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, '_curl_read_headers'));
02184
02185 if ( is_array($cookies) ) {
02186 curl_setopt($ch,CURLOPT_COOKIE,implode(';',$cookies));
02187 }
02188
02189 if ($this->hasSA()) {
02190 $more_headers = array ("soapaction: http://www.oasis-open.org/committees/security",
02191 "cache-control: no-cache",
02192 "pragma: no-cache",
02193 "accept: text/xml",
02194 "connection: keep-alive",
02195 "content-type: text/xml");
02196
02197 curl_setopt($ch, CURLOPT_HTTPHEADER, $more_headers);
02198 curl_setopt($ch, CURLOPT_POST, 1);
02199 $data = $this->buildSAMLPayload();
02200
02201 curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
02202 }
02203
02204 $buf = curl_exec ($ch);
02205
02206 if ( $buf === FALSE ) {
02207 phpCAS::trace('curl_exec() failed');
02208 $err_msg = 'CURL error #'.curl_errno($ch).': '.curl_error($ch);
02209
02210
02211 curl_close ($ch);
02212 $res = FALSE;
02213 } else {
02214
02215 curl_close ($ch);
02216
02217 $headers = $this->_curl_headers;
02218 $body = $buf;
02219 }
02220
02221 phpCAS::traceEnd($res);
02222 return $res;
02223 }
02224
02232 function buildSAMLPayload()
02233 {
02234 phpCAS::traceBegin();
02235
02236
02237 $sa = $this->getSA();
02238
02239
02240 $body=SAML_SOAP_ENV.SAML_SOAP_BODY.SAMLP_REQUEST.SAML_ASSERTION_ARTIFACT.$sa.SAML_ASSERTION_ARTIFACT_CLOSE.SAMLP_REQUEST_CLOSE.SAML_SOAP_BODY_CLOSE.SAML_SOAP_ENV_CLOSE;
02241
02242 phpCAS::traceEnd($body);
02243 return ($body);
02244 }
02245
02249 var $_curl_headers = array();
02250 function _curl_read_headers($ch, $header)
02251 {
02252 $this->_curl_headers[] = $header;
02253 return strlen($header);
02254 }
02255
02271 function serviceWeb($url,&$err_code,&$output)
02272 {
02273 phpCAS::traceBegin();
02274
02275 $pt = $this->retrievePT($url,$err_code,$output);
02276
02277 $res = TRUE;
02278
02279
02280 if ( !$pt ) {
02281
02282 phpCAS::trace('PT was not retrieved correctly');
02283 $res = FALSE;
02284 } else {
02285
02286 $cookies = $this->getCookies($url);
02287
02288
02289 if ( strstr($url,'?') === FALSE ) {
02290 $service_url = $url.'?ticket='.$pt;
02291 } else {
02292 $service_url = $url.'&ticket='.$pt;
02293 }
02294
02295 phpCAS::trace('reading URL`'.$service_url.'\'');
02296 if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) {
02297 phpCAS::trace('could not read URL`'.$service_url.'\'');
02298 $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
02299
02300 $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
02301 $service_url,
02302 $err_msg);
02303 $res = FALSE;
02304 } else {
02305
02306 phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:');
02307 $this->setCookies($headers,$url);
02308 // Check for a possible redirect (phpCAS authenticiation redirect after ticket removal)
02309 foreach($headers as $header){
02310 if (preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches))
02311 {
02312 $redirect_url = trim(array_pop($matches));
02313 phpCAS :: trace('Found redirect:'.$redirect_url);
02314 $cookies = $this->getCookies($redirect_url);
02315 phpCAS::trace('reading URL`'.$redirect_url.'\'');
02316 if ( !$this->readURL($redirect_url,$cookies,$headers,$output,$err_msg) ) {
02317 phpCAS::trace('could not read URL`'.$redirect_url.'\'');
02318 $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
02319
02320 $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
02321 $service_url,
02322 $err_msg);
02323 $res = FALSE;
02324 } else {
02325
02326 phpCAS::trace('URL`'.$redirect_url.'\' has been read, storing cookies:');
02327 $this->setCookies($headers,$redirect_url);
02328 }
02329 break;
02330 }
02331 }
02332 }
02333 }
02334
02335 phpCAS::traceEnd($res);
02336 return $res;
02337 }
02338
02345 function setCookies($headers,$url){
02346 phpCAS::traceBegin();
02347 foreach ( $headers as $header ) {
02348 // test if the header is a cookie
02349 if ( preg_match('/^Set-Cookie:/',$header) ) {
02350 // the header is a cookie, remove the beginning
02351 $header_val = preg_replace('/^Set-Cookie: */','',$header);
02352 // extract interesting information
02353 $name_val = strtok($header_val,'; ');
02354 // extract the name and the value of the cookie
02355 $cookie_name = strtok($name_val,'=');
02356 $cookie_val = strtok('=');
02357 // store the cookie
02358 $_SESSION['phpCAS']['services'][$url]['cookies'][$cookie_name] = $cookie_val;
02359 phpCAS::trace($cookie_name.' -> '.$cookie_val);
02360 }
02361 }
02362 phpCAS::traceEnd();
02363 }
02364
02369 function getCookies($url){
02370 $cookies = array();
02371 if ( isset($_SESSION['phpCAS']['services'][$url]['cookies']) &&
02372 is_array($_SESSION['phpCAS']['services'][$url]['cookies']) ) {
02373 foreach ( $_SESSION['phpCAS']['services'][$url]['cookies'] as $name => $val ) {
02374 $cookies[] = $name.'='.$val;
02375 }
02376 }
02377 return $cookies;
02378 }
02379
02399 function serviceMail($url,$service,$flags,&$err_code,&$err_msg,&$pt)
02400 {
02401 phpCAS::traceBegin();
02402 // at first retrieve a PT
02403 $pt = $this->retrievePT($service,$err_code,$output);
02404
02405 $stream = FALSE;
02406
02407 // test if PT was retrieved correctly
02408 if ( !$pt ) {
02409 // note: $err_code and $err_msg are filled by CASClient::retrievePT()
02410 phpCAS::trace('PT was not retrieved correctly');
02411 } else {
02412 phpCAS::trace('opening IMAP URL `'.$url.'\'...');
02413 $stream = @imap_open($url,$this->getUser(),$pt,$flags);
02414 if ( !$stream ) {
02415 phpCAS::trace('could not open URL');
02416 $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
02417
02418 $err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
02419 $service_url,
02420 var_export(imap_errors(),TRUE));
02421 $pt = FALSE;
02422 $stream = FALSE;
02423 } else {
02424 phpCAS::trace('ok');
02425 }
02426 }
02427
02428 phpCAS::traceEnd($stream);
02429 return $stream;
02430 }
02431
02434
02435
02436
02437
02438
02439
02440
02441
02442
02456 var $_pt = '';
02457
02463 function getPT()
02464 {
02465
02466 return $this->_pt;
02467 }
02468
02474 function setPT($pt)
02475 { $this->_pt = $pt; }
02476
02482 function hasPT()
02483 { return !empty($this->_pt); }
02489 function getSA()
02490 { return 'ST'.substr($this->_sa, 2); }
02491
02497 function setSA($sa)
02498 { $this->_sa = $sa; }
02499
02505 function hasSA()
02506 { return !empty($this->_sa); }
02507
02509
02510
02511
02524 function validatePT(&$validate_url,&$text_response,&$tree_response)
02525 {
02526 phpCAS::traceBegin();
02527
02528 $validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
02529
02530 if ( $this->isProxy() ) {
02531
02532 $validate_url .= '&pgtUrl='.urlencode($this->getCallbackURL());
02533 }
02534
02535
02536 if ( !$this->readURL($validate_url,'',$headers,$text_response,$err_msg) ) {
02537 phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
02538 $this->authError('PT not validated',
02539 $validate_url,
02540 TRUE/*$no_response*/);
02541 }
02542
02543 // read the response of the CAS server into a DOM object
02544 if ( !($dom = domxml_open_mem($text_response))) {
02545 // read failed
02546 $this->authError('PT not validated',
02547 $validate_url,
02548 FALSE/*$no_response*/,
02549 TRUE/*$bad_response*/,
02550 $text_response);
02551 }
02552 // read the root node of the XML tree
02553 if ( !($tree_response = $dom->document_element()) ) {
02554 // read failed
02555 $this->authError('PT not validated',
02556 $validate_url,
02557 FALSE/*$no_response*/,
02558 TRUE/*$bad_response*/,
02559 $text_response);
02560 }
02561 // insure that tag name is 'serviceResponse'
02562 if ( $tree_response->node_name() != 'serviceResponse' ) {
02563 // bad root node
02564 $this->authError('PT not validated',
02565 $validate_url,
02566 FALSE/*$no_response*/,
02567 TRUE/*$bad_response*/,
02568 $text_response);
02569 }
02570 if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
02571 // authentication succeded, extract the user name
02572 if ( sizeof($arr = $tree_response->get_elements_by_tagname("user")) == 0) {
02573 // no user specified => error
02574 $this->authError('PT not validated',
02575 $validate_url,
02576 FALSE/*$no_response*/,
02577 TRUE/*$bad_response*/,
02578 $text_response);
02579 }
02580 $this->setUser(trim($arr[0]->get_content()));
02581
02582 } else if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
02583 // authentication succeded, extract the error code and message
02584 $this->authError('PT not validated',
02585 $validate_url,
02586 FALSE/*$no_response*/,
02587 FALSE/*$bad_response*/,
02588 $text_response,
02589 $arr[0]->get_attribute('code')/*$err_code*/,
02590 trim($arr[0]->get_content())/*$err_msg*/);
02591 } else {
02592 $this->authError('PT not validated',
02593 $validate_url,
02594 FALSE/*$no_response*/,
02595 TRUE/*$bad_response*/,
02596 $text_response);
02597 }
02598
02599 $this->renameSession($this->getPT());
02600 // at this step, PT has been validated and $this->_user has been set,
02601
02602 phpCAS::traceEnd(TRUE);
02603 return TRUE;
02604 }
02605
02608 // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
02609 // XX XX
02610 // XX MISC XX
02611 // XX XX
02612 // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
02613
02619 // ########################################################################
02620 // URL
02621 // ########################################################################
02629 var $_url = '';
02630
02639 function getURL()
02640 {
02641 phpCAS::traceBegin();
02642 // the URL is built when needed only
02643 if ( empty($this->_url) ) {
02644 $final_uri = '';
02645 // remove the ticket if present in the URL
02646 $final_uri = ($this->isHttps()) ? 'https' : 'http';
02647 $final_uri .= ':
02648
02649
02650
02651 if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
02652
02653
02654
02655 if (empty($_SERVER['SERVER_NAME'])) {
02656 $server_name = $_SERVER['HTTP_HOST'];
02657 } else {
02658 $server_name = $_SERVER['SERVER_NAME'];
02659 }
02660 } else {
02661 $server_name = $_SERVER['HTTP_X_FORWARDED_SERVER'];
02662 }
02663 $final_uri .= $server_name;
02664 if (!strpos($server_name, ':')) {
02665 if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
02666 || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
02667 $final_uri .= ':';
02668 $final_uri .= $_SERVER['SERVER_PORT'];
02669 }
02670 }
02671
02672 $request_uri = explode('?', $_SERVER['REQUEST_URI'], 2);
02673 $final_uri .= $request_uri[0];
02674
02675 if (isset($request_uri[1]) && $request_uri[1])
02676 {
02677 $query_string = $this->removeParameterFromQueryString('ticket', $request_uri[1]);
02678
02679
02680 if ($query_string !== '')
02681 $final_uri .= "?$query_string";
02682
02683 }
02684
02685 phpCAS::trace("Final URI: $final_uri");
02686 $this->setURL($final_uri);
02687 }
02688 phpCAS::traceEnd($this->_url);
02689 return $this->_url;
02690 }
02691
02692
02693
02703 function removeParameterFromQueryString($parameterName, $queryString)
02704 {
02705 $parameterName = preg_quote($parameterName);
02706 return preg_replace("/&$parameterName(=[^&]*)?|^$parameterName(=[^&]*)?&?/", '', $queryString);
02707 }
02708
02709
02717 function setURL($url)
02718 {
02719 $this->_url = $url;
02720 }
02721
02722
02723
02724
02740 function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='')
02741 {
02742 phpCAS::traceBegin();
02743
02744 $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED));
02745 printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),htmlentities($this->getURL()),$_SERVER['SERVER_ADMIN']);
02746 phpCAS::trace('CAS URL: '.$cas_url);
02747 phpCAS::trace('Authentication failure: '.$failure);
02748 if ( $no_response ) {
02749 phpCAS::trace('Reason: no response from the CAS server');
02750 } else {
02751 if ( $bad_response ) {
02752 phpCAS::trace('Reason: bad response from the CAS server');
02753 } else {
02754 switch ($this->getServerVersion()) {
02755 case CAS_VERSION_1_0:
02756 phpCAS::trace('Reason: CAS error');
02757 break;
02758 case CAS_VERSION_2_0:
02759 if ( empty($err_code) )
02760 phpCAS::trace('Reason: no CAS error');
02761 else
02762 phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg);
02763 break;
02764 }
02765 }
02766 phpCAS::trace('CAS response: '.$cas_response);
02767 }
02768 $this->printHTMLFooter();
02769 phpCAS::traceExit();
02770 exit();
02771 }
02772
02774 }
02775
02776 ?>