From 634a496d01950b9ff791d3bc99accece43a3dd4f Mon Sep 17 00:00:00 2001 From: Jacek Kowalski <Jacek@jacekk.info> Date: Mon, 05 Oct 2020 15:46:45 +0000 Subject: [PATCH] Fix session handling die to changes in PHP 7.1 --- uphpCAS.php | 258 +++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 197 insertions(+), 61 deletions(-) diff --git a/uphpCAS.php b/uphpCAS.php index bce5aab..bfb2b69 100644 --- a/uphpCAS.php +++ b/uphpCAS.php @@ -1,7 +1,7 @@ <?php // Thrown when internal error occurs class JasigException extends Exception {} -// Thrown when CAS server return authentication error +// Thrown when CAS server returns authentication error class JasigAuthException extends JasigException {} class JasigUser { @@ -13,8 +13,12 @@ const VERSION = '1.0'; protected $serverUrl = ''; protected $serviceUrl; + protected $sessionName = 'uphpCAS-user'; + protected $sessionStarted = FALSE; + protected $method = 'POST'; + protected $caFile = NULL; - function __construct($serverUrl = NULL, $serviceUrl = NULL) { + function __construct($serverUrl = NULL, $serviceUrl = NULL, $sessionName = NULL) { if($serverUrl != NULL) { $this->serverUrl = rtrim($serverUrl, '/'); } @@ -22,60 +26,149 @@ if($serviceUrl != NULL) { $this->serviceUrl = $serviceUrl; } else { - $url = 'http://'; - $port = 0; - if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { - $url = 'https://'; - if(isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] != '443') { - $port = $_SERVER['SERVER_PORT']; - } - } elseif(isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] != '80') { - $port = $_SERVER['SERVER_PORT']; - } - - $url .= $_SERVER['SERVER_NAME']; - - if($port != 0) { - $url .= ':'.$port; - } - $url .= $_SERVER['REQUEST_URI']; - - $this->serviceUrl = $url; + $this->serviceUrl = $this->getCurrentUrl(); + } + + if($sessionName) { + $this->sessionName = $sessionName; + } + + if(version_compare(PHP_VERSION, '5.6', '<')) { + $this->caFile = $this->findCaFile(); } } + public function getCurrentUrl() { + $url = 'http://'; + $port = 0; + if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { + $url = 'https://'; + if(isset($_SERVER['SERVER_PORT']) + && $_SERVER['SERVER_PORT'] != '443') { + $port = $_SERVER['SERVER_PORT']; + } + } elseif(isset($_SERVER['SERVER_PORT']) + && $_SERVER['SERVER_PORT'] != '80') { + $port = $_SERVER['SERVER_PORT']; + } + + $url .= $_SERVER['SERVER_NAME']; + + if($port != 0) { + $url .= ':'.$port; + } + + $url .= $_SERVER['REQUEST_URI']; + + if(isset($_GET['ticket'])) { + $pos = max( + strrpos($url, '?ticket='), + strrpos($url, '&ticket=') + ); + $url = substr($url, 0, $pos); + } + + return $url; + } + + public function getServerUrl() { + return $this->serverUrl; + } public function setServerUrl($serverUrl) { $this->serverUrl = $serverUrl; } + public function getServiceUrl() { + return $this->serviceUrl; + } public function setServiceUrl($serviceUrl) { $this->serviceUrl = $serviceUrl; } - public function loginUrl() { - return $this->serverUrl.'/login?service='.urlencode($this->serviceUrl); + public function getSessionName() { + return $this->sessionName; + } + public function setSessionName($sessionName) { + $this->sessionName = $sessionName; } - public function logoutUrl() { - return $this->serverUrl.'/logout'; + public function getMethod() { + return $this->method; } - - public function logout() { - session_start(); - if(isset($_SESSION['uphpCAS-user'])) { - unset($_SESSION['uphpCAS-user']); + public function setMethod($method) { + if($method != 'GET' && $method != 'POST') { + throw new DomainException('Unsupported CAS response' + .' method: '.$method); } - header('Location: '.$this->logoutUrl()); - die(); + $this->method = $method; + } + + public function getCaFile() { + return $this->caFile; + } + public function setCaFile($caFile) { + if(!is_file($caFile)) { + throw new DomainException('Invalid CA file: '.$caFile); + } + $this->caFile = $caFile; + } + + public function session_start() { + if($this->sessionStarted) { + return TRUE; + } + if(version_compare(PHP_VERSION, '7.1.0', '<')) { + @session_start(); + } else { + if(!isset($_SESSION)) { + if(!session_start()) { + throw new RuntimeException('Cannot start session'); + } + } + } + $this->sessionStarted = TRUE; + return TRUE; + } + + public function loginUrl() { + return $this->serverUrl.'/login?method='.$this->method + .'&service='.urlencode($this->serviceUrl); + } + + public function logoutUrl($returnUrl = NULL) { + return $this->serverUrl.'/logout' + .($returnUrl ? '?service='.urlencode($returnUrl) : ''); + } + + public function logoutLocal() { + $this->session_start(); + unset($_SESSION[$this->sessionName]); + } + + public function logout($returnUrl = NULL) { + $this->logoutLocal(); + if($this->isAuthenticated()) { + header('Location: '.$this->logoutUrl($returnUrl)); + die(); + } elseif($returnUrl) { + header('Location: '.$returnUrl); + die(); + } + } + + public function isAuthenticated() { + $this->session_start(); + return isset($_SESSION[$this->sessionName]); } public function authenticate() { - session_start(); - if(isset($_SESSION['uphpCAS-user'])) { - return $_SESSION['uphpCAS-user']; - } elseif(isset($_GET['ticket'])) { - $user = $this->verifyTicket($_GET['ticket']); - $_SESSION['uphpCAS-user'] = $user; + $this->session_start(); + if($this->isAuthenticated()) { + return $_SESSION[$this->sessionName]; + } elseif(isset($_REQUEST['ticket'])) { + $user = $this->verifyTicket($_REQUEST['ticket']); + session_regenerate_id(); + $_SESSION[$this->sessionName] = $user; return $user; } else { header('Location: '.$this->loginUrl()); @@ -83,7 +176,25 @@ } } - public function verifyTicket($ticket) { + protected function findCaFile() { + $cafiles = array( + '/etc/ssl/certs/ca-certificates.crt', + '/etc/ssl/certs/ca-bundle.crt', + '/etc/pki/tls/certs/ca-bundle.crt', + ); + + $cafile = NULL; + foreach($cafiles as $file) { + if(is_file($file)) { + $cafile = $file; + break; + } + } + + return $cafile; + } + + protected function createStreamContext($hostname) { $context = array( 'http' => array( 'method' => 'GET', @@ -92,14 +203,33 @@ ), 'ssl' => array( 'verify_peer' => TRUE, - 'allow_self_signed' => FALSE, + 'verify_peer_name' => TRUE, 'verify_depth' => 5, - 'ciphers' => 'HIGH:-MD5:-aNULL:-DES', + 'allow_self_signed' => FALSE, + 'disable_compression' => TRUE, ), ); - $data = file_get_contents($this->serverUrl.'/serviceValidate?service='.urlencode($this->serviceUrl).'&ticket='.urlencode($ticket), - FALSE, stream_context_create($context)); + if($this->caFile) { + $context['ssl']['cafile'] = $this->caFile; + } + + if(version_compare(PHP_VERSION, '5.6', '<')) { + $context['ssl']['ciphers'] = 'ECDH:DH:AES:CAMELLIA:!SSLv2:!aNULL' + .':!eNULL:!EXPORT:!DES:!3DES:!MD5:!RC4:!ADH:!PSK:!SRP'; + $context['ssl']['CN_match'] = $hostname; + } + + return stream_context_create($context); + } + + public function verifyTicket($ticket) { + $url = parse_url($this->serverUrl); + $context = $this->createStreamContext($url['host']); + + $data = file_get_contents($this->serverUrl + .'/serviceValidate?service='.urlencode($this->serviceUrl) + .'&ticket='.urlencode($ticket), FALSE, $context); if($data === FALSE) { throw new JasigException('Authentication error: CAS server is unavailable'); } @@ -111,24 +241,26 @@ $xml->loadXML($data); foreach(libxml_get_errors() as $error) { - $e = new ErrorException($error->message, $error->code, 1, $error->file, $error->line); + $e = new ErrorException($error->message, $error->code, 1, + $error->file, $error->line); switch ($error->level) { case LIBXML_ERR_ERROR: - throw new Exception('Fatal error during XML parsing', 0, $e); - break; case LIBXML_ERR_FATAL: - throw new Exception('Fatal error during XML parsing', 0, $e); + throw new Exception('Fatal error during XML parsing', + 0, $e); break; } } - } - catch(Exception $e) { - throw new JasigException('Authentication error: CAS server response invalid - parse error', 0, $e); - } finally { + } catch(Exception $e) { libxml_clear_errors(); libxml_disable_entity_loader($xmlEntityLoader); libxml_use_internal_errors($xmlInternalErrors); + throw new JasigException('Authentication error: CAS server' + .' response invalid - parse error', 0, $e); } + libxml_clear_errors(); + libxml_disable_entity_loader($xmlEntityLoader); + libxml_use_internal_errors($xmlInternalErrors); $failure = $xml->getElementsByTagName('authenticationFailure'); $success = $xml->getElementsByTagName('authenticationSuccess'); @@ -136,23 +268,28 @@ if($failure->length > 0) { $failure = $failure->item(0); if(!($failure instanceof DOMElement)) { - throw new JasigException('Authentication error: CAS server response invalid - authenticationFailure'); + throw new JasigException('Authentication error: CAS server' + .' response invalid - authenticationFailure'); } - throw new JasigAuthException('Authentication error: '.$failure->textContent); + throw new JasigAuthException('Authentication error: ' + .$failure->textContent); } elseif($success->length > 0) { $success = $success->item(0); if(!($success instanceof DOMElement)) { - throw new JasigException('Authentication error: CAS server response invalid - authenticationSuccess'); + throw new JasigException('Authentication error: CAS server' + .' response invalid - authenticationSuccess'); } $user = $success->getElementsByTagName('user'); if($user->length == 0) { - throw new JasigException('Authentication error: CAS server response invalid - user'); + throw new JasigException('Authentication error: CAS server' + .' response invalid - user'); } $user = trim($user->item(0)->textContent); - if(strlen($user)<1) { - throw new JasigException('Authentication error: CAS server response invalid - user value'); + if(strlen($user) < 1) { + throw new JasigException('Authentication error: CAS server' + .' response invalid - user value'); } $jusr = new JasigUser(); @@ -167,10 +304,9 @@ } return $jusr; - } - else - { - throw new JasigException('Authentication error: CAS server response invalid - required tag not found'); + } else { + throw new JasigException('Authentication error: CAS server' + .' response invalid - required tag not found'); } } } -- Gitblit v1.9.1