$value){ if (!isset($$key)){ $$key = $value; } } // Set dependent default configuration options $defaults = array(); $defaults['redirectCookieName'] = $cookieNamePrefix.'_redirect_user_idp'; $defaults['redirectStateCookieName'] = $cookieNamePrefix.'_redirection_state'; $defaults['SAMLDomainCookieName'] = $cookieNamePrefix.'_saml_idp'; $defaults['SPCookieName'] = $cookieNamePrefix.'_saml_sp'; $defaults['logoURL'] = $imageURL.'/switch-aai-transparent.png'; $defaults['smallLogoURL'] = $imageURL.'/switch-aai-transparent-small.png'; // Initialize dependent defaults foreach($defaults as $key => $value){ if (!isset($$key)){ $$key = $value; } } } /******************************************************************************/ // Generates an array of IDPs using the cookie value function getIdPArrayFromValue($value){ // Decodes and splits cookie value $CookieArray = preg_split('/ /', $value); $CookieArray = array_map('base64_decode', $CookieArray); return $CookieArray; } /******************************************************************************/ // Generate the value that is stored in the cookie using the list of IDPs function getValueFromIdPArray($CookieArray){ // Merges cookie content and encodes it $CookieArray = array_map('base64_encode', $CookieArray); $value = implode(' ', $CookieArray); return $value; } /******************************************************************************/ // Append a value to the array of IDPs function appendValueToIdPArray($value, $CookieArray){ // Remove value if it already existed in array foreach (array_keys($CookieArray) as $i){ if ($CookieArray[$i] == $value){ unset($CookieArray[$i]); } } // Add value to end of array $CookieArray[] = $value; return $CookieArray; } /******************************************************************************/ // Checks if the configuration file has changed. If it has, check the file // and change its timestamp. function checkConfig($IDPConfigFile, $backupIDPConfigFile){ // Do files have the same modification time if (filemtime($IDPConfigFile) == filemtime($backupIDPConfigFile)) return true; // Availability check if (!file_exists($IDPConfigFile)) return false; // Readability check if (!is_readable($IDPConfigFile)) return false; // Size check if (filesize($IDPConfigFile) < 200) return false; // Make modification time the same // If that doesnt work we won't notice it touch ($IDPConfigFile, filemtime($backupIDPConfigFile)); return true; } /******************************************************************************/ // Checks if an IDP exists and returns true if it does, false otherwise function checkIDP($IDP){ global $IDProviders; if (isset($IDProviders[$IDP])){ return true; } else { return false; } } /******************************************************************************/ // Checks if an IDP exists and returns true if it exists and prints an error // if it doesnt function checkIDPAndShowErrors($IDP){ global $IDProviders; if (checkIDP($IDP)){ return true; } // Otherwise show an error $message = sprintf(getLocalString('invalid_user_idp'), htmlentities($IDP))."

\n"; foreach ($IDProviders as $key => $value){ if (isset($value['SSO'])){ $message .= $key."
\n"; } } $message .= "
\n"; printError($message); exit; } /******************************************************************************/ // Validates the URL format and returns the URL without GET arguments and fragment function verifyAndStripReturnURL($url){ $components = parse_url($url); if (!$components){ return false; } $recomposedURL = $components['scheme'].'://'; if (isset($components['user'])){ $recomposedURL .= $components['user']; if (isset($components['pass'])){ $recomposedURL .= ':'.$components['pass']; } $recomposedURL .= '@'; } if (isset($components['host'])){ $recomposedURL .= $components['host']; } if (isset($components['port'])){ $recomposedURL .= ':'.$components['port']; } if (isset($components['path'])){ $recomposedURL .= $components['path']; } return $recomposedURL; } /******************************************************************************/ // Parses the hostname out of a string and returns it function getHostNameFromURI($string){ // Check if string is URN if (preg_match('/^urn:mace:/i', $string)){ // Return last component of URN return end(explode(':', $string)); } // Apparently we are dealing with something like a URL if (preg_match('/([a-zA-Z0-9\-\.]+\.[a-zA-Z0-9\-\.]{2,6})/', $string, $matches)){ return $matches[0]; } else { return ''; } } /******************************************************************************/ // Parses the domain out of a string and returns it function getDomainNameFromURI($string){ // Check if string is URN if (preg_match('/^urn:mace:/i', $string)){ // Return last component of URN return getTopLevelDomain(end(explode(':', $string))); } // Apparently we are dealing with something like a URL if (preg_match('/[a-zA-Z0-9\-\.]+\.([a-zA-Z0-9\-\.]{2,6})/', $string, $matches)){ return getTopLevelDomain($matches[0]); } else { return ''; } } /******************************************************************************/ // Returns top level domain name from a DNS name function getTopLevelDomain($string){ $hostnameComponents = explode('.', $string); if (count($hostnameComponents) >= 2){ return $hostnameComponents[count($hostnameComponents)-2].'.'.$hostnameComponents[count($hostnameComponents)-1]; } else { return $string; } } /******************************************************************************/ // Parses the reverse dns lookup hostname out of a string and returns domain function getDomainNameFromURIHint(){ global $IDProviders; $clientHostname = gethostbyaddr($_SERVER['REMOTE_ADDR']); if ($clientHostname == $_SERVER['REMOTE_ADDR']){ return '-'; } // Get domain name from client host name $clientDomainName = getDomainNameFromURI($clientHostname); if ($clientDomainName == ''){ return '-'; } // Return first matching IdP entityID that contains the client domain name foreach ($IDProviders as $key => $value){ if ( preg_match('/^http.+'.$clientDomainName.'/', $key) || preg_match('/^urn:.+'.$clientDomainName.'$/', $key)){ return $key; } } // No matching entityID was found return '-'; } /******************************************************************************/ // Get the user's language using the accepted language http header function determineLanguage(){ global $langStrings, $defaultLanguage; // Check if language is enforced by PATH-INFO argument if (isset($_SERVER['PATH_INFO']) && !empty($_SERVER['PATH_INFO'])){ foreach ($langStrings as $lang => $values){ if (preg_match('#/'.$lang.'($|/)#',$_SERVER['PATH_INFO'])){ return $lang; } } } // Check if there is a language GET argument if (isset($_GET['lang'])){ $localeComponents = decomposeLocale($_GET['lang']); if ( $localeComponents !== false && isset($langStrings[$localeComponents[0]]) ){ // Return language return $localeComponents[0]; } } // Return default language if no headers are present otherwise if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])){ return $defaultLanguage; } // Inspect Accept-Language header which looks like: // Accept-Language: en,de-ch;q=0.8,fr;q=0.7,fr-ch;q=0.5,en-us;q=0.3,de;q=0.2 $languages = explode( ',', trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])); foreach ($languages as $language){ $languageParts = explode(';', $language); // Only treat art before the prioritization $localeComponents = decomposeLocale($languageParts[0]); if ( $localeComponents !== false && isset($langStrings[$localeComponents[0]]) ){ // Return language return $localeComponents[0]; } } return $defaultLanguage; } /******************************************************************************/ // Splits up a string (relazed) according to // http://www.debian.org/doc/manuals/intro-i18n/ch-locale.en.html#s-localename // and returns an array with the four components function decomposeLocale($locale){ // Locale name syntax: language[_territory][.codeset][@modifier] if (!preg_match('/^([a-zA-Z]{2})([-_][a-zA-Z]{2})?(\.[^@]+)?(@.+)?$/', $locale, $matches)){ return false; } else { // Remove matched string in first position array_shift($matches); return $matches; } } /******************************************************************************/ // Gets a string in the user's language. If no localized version is available // for the string, the English string is returned as default. function getLocalString($string, $encoding = ''){ global $defaultLanguage, $langStrings, $language; $textString = ''; if (isset($langStrings[$language][$string])){ $textString = $langStrings[$language][$string]; } else { $textString = $langStrings[$defaultLanguage][$string]; } // Change encoding if necessary if ($encoding == 'js'){ $textString = convertToJSString($textString); } return $textString; } /******************************************************************************/ // Converts string to a JavaScript format that can be used in JS alert function convertToJSString($string){ return addslashes(html_entity_decode($string, ENT_COMPAT, 'UTF-8')); } /******************************************************************************/ // Checks if entityID hostname of a valid IdP exists in path info function getIdPPathInfoHint(){ global $IDProviders; // Check if path info is available at all if (!isset($_SERVER['PATH_INFO']) || empty($_SERVER['PATH_INFO'])){ return '-'; } // Check for entityID hostnames of all available IdPs foreach ($IDProviders as $key => $value){ // Only check actual IdPs if ( isset($value['SSO']) && !empty($value['SSO']) && $value['Type'] != 'wayf' && isPartOfPathInfo(getHostNameFromURI($key)) ){ return $key; } } // Check for entityID domain names of all available IdPs foreach ($IDProviders as $key => $value){ // Only check actual IdPs if ( isset($value['SSO']) && !empty($value['SSO']) && $value['Type'] != 'wayf' && isPartOfPathInfo(getDomainNameFromURI($key)) ){ return $key; } } return '-'; } /******************************************************************************/ // Parses the Kerbores realm out of the string and returns it function composeOptionTitle($IdPValues){ $title = ''; foreach($IdPValues as $key => $value){ if (is_array($value) && isset($value['Name'])){ $title .= ' '.$value['Name']; } elseif (is_array($value) && isset($value['Keywords'])) { $title .= ' '.$value['Keywords']; } } return $title; } /******************************************************************************/ // Parses the Kerbores realm out of the string and returns it function getKerberosRealm($string){ global $IDProviders; if ($string !='' ) { // Find a matching Kerberos realm foreach ($IDProviders as $key => $value){ if ($value['Realm'] == $string) return $key; } } return '-'; } /******************************************************************************/ // Determines the IdP according to the IP address if possible function getIPAdressHint() { global $IDProviders; foreach($IDProviders as $name => $idp) { if (is_array($idp) && array_key_exists("IP", $idp)) { $clientIP = $_SERVER["REMOTE_ADDR"]; foreach( $idp["IP"] as $network ) { if (isIPinCIDRBlock($network, $clientIP)) { return $name; } } } } return '-'; } /******************************************************************************/ // Returns true if IP is in IPv4/IPv6 CIDR range // and returns false otherwise function isIPinCIDRBlock($cidr, $ip) { // Split CIDR notation list ($net, $mask) = preg_split ("|/|", $cidr); // Convert to binary string value of 1s and 0s $netAsBinary = convertIPtoBinaryForm($net); $ipAsBinary = convertIPtoBinaryForm($ip); // Return false if netmask and ip are using different protocols if (strlen($netAsBinary) != strlen($ipAsBinary)){ return false; } // Compare the first $mask bits for($i = 0; $i < $mask; $i++){ // Return false if bits don't match if ($netAsBinary[$i] != $ipAsBinary[$i]){ return false; } } // If we got here, ip matches net return true; } /******************************************************************************/ // Converts IP in human readable format to binary string function convertIPtoBinaryForm($ip){ // Handle IPv4 IP if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false){ return base_convert(ip2long($ip),10,2); } // Return false if IP is neither IPv4 nor a IPv6 IP if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false){ return false; } // Convert IP to binary structure and return false if this fails if(($ipAsBinStructure = inet_pton($ip)) === false) { return false; } $numOfBytes = 16; $ipAsBinaryString = ''; // Convert IP to binary string while ($numOfBytes > 0){ // Convert current byte to decimal number $currentByte = ord($ipAsBinStructure[$numOfBytes - 1]); // Convert currenty byte to string of 1 and 0 $currentByteAsBinary = sprintf("%08b", $currentByte); // Prepend to rest of IP in binary string $ipAsBinaryString = $currentByteAsBinary.$ipAsBinaryString; // Decrease byte counter $numOfBytes--; } return $ipAsBinaryString; } /******************************************************************************/ // Returns true if URL could be verified or if no check is necessary, false otherwise function verifyReturnURL($entityID, $returnURL) { global $SProviders, $useACURLsForReturnParamCheck; // If SP has a , check return param if (isset($SProviders[$entityID]['DSURL'])){ return in_array($returnURL, $SProviders[$entityID]['DSURL']); } // If fall back check is enabled, check return param if ($useACURLsForReturnParamCheck){ // Return true if no assertion consumer URL is defined to check against // Should never happend if (!isset($SProviders[$entityID]['ACURL'])){ return false; } $returnURLHostName = getHostNameFromURI($returnURL); foreach($SProviders[$entityID]['ACURL'] as $ACURL){ if (getHostNameFromURI($ACURL) == $returnURLHostName){ return true; } } // We haven't found a matchin assertion consumer url so we return false return false; } // SP has no and $useACURLsForReturnParamCheck // is disabled, so we don't check anything return true; } /******************************************************************************/ // Returns a reasonable value for returnIDParam function getReturnIDParam() { if (isset($_GET['returnIDParam']) && !empty($_GET['returnIDParam'])){ return $_GET['returnIDParam']; } else { return 'entityID'; } } /******************************************************************************/ // Returns true if valid Shibboleth 1.x request or Directory Service request function isValidShibRequest(){ return (isValidShib1Request() || isValidDSRequest()); } /******************************************************************************/ // Returns true if valid Shibboleth request function isValidShib1Request(){ if (isset($_GET['shire']) && isset($_GET['target'])){ return true; } else { return false; } } /******************************************************************************/ // Returns true if request is a valid Directory Service request function isValidDSRequest(){ global $SProviders; // If entityID is not present, request is invalid if (!isset($_GET['entityID'])){ return false; } // If entityID and return parameters are present, request is valid if (isset($_GET['return'])){ return true; } // If no return parameter and no Discovery Service endpoint is available // for SP, request is invalid if (!isset($SProviders[$_GET['entityID']]['DSURL'])){ return false; } if (count($SProviders[$_GET['entityID']]['DSURL']) < 1){ return false; } // EntityID is available and there is at least one DiscoveryService // endpoint defined. Therefore, the request is valid return true; } /******************************************************************************/ // Sets the Location header to redirect the user's web browser function redirectTo($url){ header('Location: '.$url); } /******************************************************************************/ // Sets the Location that is used for redirect the web browser back to the SP function redirectToSP($url, $IdP){ if (preg_match('/\?/', $url) > 0){ redirectTo($url.'&'.getReturnIDParam().'='.urlencode($IdP)); } else { redirectTo($url.'?'.getReturnIDParam().'='.urlencode($IdP)); } } /******************************************************************************/ // Returns true if valid Directory Service request function logAccessEntry($protocol, $type, $sp, $idp){ global $WAYFLogFile, $useLogging; if (!$useLogging){ return; } // Let's make sure the file exists and is writable first. if (is_writable($WAYFLogFile)) { // Create log entry $entry = date('Y-m-d H:i:s').' '.$_SERVER['REMOTE_ADDR'].' '.$protocol.' '.$type.' '.$idp.' '.$sp."\n"; // We are opening $filename in append mode. // The file pointer is at the bottom of the file hence // that's where $somecontent will go when we fwrite() it. if (!$handle = fopen($WAYFLogFile, 'a')) { return; } // Try getting lock while (!flock($handle, LOCK_EX)){ usleep(rand(10, 100)); } // Write $somecontent to our opened file. fwrite($handle, $entry); // Release the lock flock($handle, LOCK_UN); // Close file handle fclose($handle); } } /******************************************************************************/ // Returns true if PATH info indicates a request of type $type function isRequestType($type){ // Make sure the type is checked at end of path info return isPartOfPathInfo($type.'$'); } /******************************************************************************/ // Checks for substrings in Path Info and returns true if match was found function isPartOfPathInfo($needle){ if ( isset($_SERVER['PATH_INFO']) && !empty($_SERVER['PATH_INFO']) && preg_match('|/'.$needle.'|', $_SERVER['PATH_INFO'])){ return true; } else { return false; } } /******************************************************************************/ // Converts to the unified datastructure that the Shibboleth DS will be using function convertToShibDSStructure($IDProviders){ global $federationName; $ShibDSIDProviders = array(); foreach ($IDProviders as $key => $value){ // Skip unknown and category entries if( !isset($value['Type']) || $value['Type'] == 'category' || $value['Type'] == 'wayf' ){ continue; } // Init and fill IdP data $identityProvider = array(); $identityProvider['entityID'] = $key; $identityProvider['DisplayNames'][] = array('lang' => 'en', 'value' => $value['Name']); // Add DisplayNames in other languages foreach($value as $lang => $name){ if( $lang == 'Name' || $lang == 'SSO' || $lang == 'Realm' || $lang == 'Type' || $lang == 'IP' ){ continue; } if (isset($name['Name'])){ $identityProvider['DisplayNames'][] = array('lang' => $lang, 'value' => $name['Name']); } } // Add data to ShibDSIDProviders $ShibDSIDProviders[] = $identityProvider; } return $ShibDSIDProviders; } /******************************************************************************/ // Sorts the IDProviders array function sortIdentityProviders(&$IDProviders){ $sortedIDProviders = Array(); $sortedCategories = Array(); foreach ($IDProviders as $entityId => $IDProvider){ if (!is_array($IDProvider) || !isset($IDProvider['Name'])){ // Remove any entries that are not arrays unset($IDProviders[$entityId]); } elseif ($IDProvider['Type'] == 'category'){ $sortedCategories[$entityId] = $IDProvider; } else { $sortedIDProviders[$entityId] = $IDProvider; } } // Sort categories and IdPs if (count($sortedCategories) > 1){ // Sort using index uasort($sortedCategories, 'sortUsingTypeIndexAndName'); } else { // Sort alphabetically using the key of a category ksort($sortedCategories); } // Add category 'unknown' if not present if (!isset($IDProviders['unknown'])){ $sortedCategories['unknown'] = array ( 'Name' => 'Unknown', 'Type' => 'category', ); } // Sort Identity Providers uasort($sortedIDProviders, 'sortUsingTypeIndexAndName'); $IDProviders = Array(); // Compose array $unknownCategoryIsEmpty = true; while(list($categoryKey, $categoryValue) = each($sortedCategories)){ $IDProviders[$categoryKey] = $categoryValue; // Loop through all IdPs foreach ($sortedIDProviders as $IDProvidersPKey => $IDProvidersValue){ // Add IdP if its type matches the current category if ($IDProvidersValue['Type'] == $categoryKey){ $IDProviders[$IDProvidersPKey] = $IDProvidersValue; unset($sortedIDProviders[$IDProvidersPKey]); } // Add IdP if its type is 'unknown' or if there doesnt exist a category for its type if ($categoryKey == 'unknown' || !isset($sortedCategories[$IDProvidersValue['Type']])){ $IDProviders[$IDProvidersPKey] = $IDProvidersValue; unset($sortedIDProviders[$IDProvidersPKey]); $unknownCategoryIsEmpty = false; } } } // Check if unkown category is needed if ($unknownCategoryIsEmpty || (count($sortedCategories) == 1)){ unset($IDProviders['unknown']); } } /******************************************************************************/ // Sorts two entries according to their Type, Index and (local) Name function sortUsingTypeIndexAndName($a, $b){ global $language; if ($a['Type'] != $b['Type']){ return strcmp($b['Type'], $a['Type']); } elseif (isset($a['Index']) && isset($b['Index']) && $a['Index'] != $b['Index']){ return strcmp($a['Index'], $b['Index']); } else { // Sort using locale names $localNameB = (isset($a[$language]['Name'])) ? $a[$language]['Name'] : $a['Name']; $localNameA = (isset($b[$language]['Name'])) ? $b[$language]['Name'] : $b['Name']; return strcmp($localNameB, $localNameA); } } /******************************************************************************/ // Returns true if the referer of the current request is matching an assertion // consumer or discovery service URL of a Service Provider function isRequestRefererMatchingSPHost(){ global $SProviders; // If referer is not available return false if (!isset($_SERVER["HTTP_REFERER"]) || $_SERVER["HTTP_REFERER"] == ''){ return false; } if (!isset($SProviders) || !is_array($SProviders)){ return false; } $refererHostname = getHostNameFromURI($_SERVER["HTTP_REFERER"]); foreach ($SProviders as $key => $SProvider){ // Check referer against entityID $spHostname = getHostNameFromURI($key); if ($refererHostname == $spHostname){ return true; } // Check referer against Discovery Response URL(DSURL) if (isset($SProvider['DSURL'])) { foreach ($SProvider['DSURL'] as $url){ $spHostname = getHostNameFromURI($url); if ($refererHostname == $spHostname){ return true; } } } // Check referer against Assertion Consumer Service URL(ACURL) if (isset($SProvider['ACURL'])) { foreach ($SProvider['ACURL'] as $url){ $spHostname = getHostNameFromURI($url); if ($refererHostname == $spHostname){ return true; } } } } return false; } ?>