diff --git a/COPYING b/COPYING index 380e3f66c14c12a9ec863f2e3574bf4061d4080b..014d808870ad7fcf47d8bf4466de1d67be569bf8 100644 --- a/COPYING +++ b/COPYING @@ -1,5 +1,9 @@ Note that some of the embedded libraries may be using other licences. In example xmlseclibs uses BSD licence. + +If you will be using simpleSAMLphp in a commercial context, please do not use +the embedded default template which contains some icons with licence that are limited +to non-commercial use. -- simpleSAMLphp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php index 03074e5c1193ca5074078783b6b2550ecfe23b7e..555628ba69baa4463d0b3542b8e291b48c6a6a63 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php @@ -214,7 +214,28 @@ class SimpleSAML_Metadata_MetaDataStorageHandler { /* We were unable to find the hostname/path in any metadata source. */ throw new Exception('Could not find any default metadata entities in set [' . $set . '] for host [' . $currenthost . ' : ' . $currenthostwithpath . ']'); } - + + /** + * This method will call getPreferredEntityIdFromCIDRhint() on all of the + * sources. + * + * @param $set Which set of metadata we are looking it up in. + * @param $ip IP address + * @return The entity id of a entity which have a CIDR hint where the provided + * IP address match. + */ + public function getPreferredEntityIdFromCIDRhint($set, $ip) { + + foreach($this->sources as $source) { + $entityId = $source->getPreferredEntityIdFromCIDRhint($set, $ip); + if($entityId !== NULL) { + return $entityId; + } + } + + return NULL; + + } /** * This function looks up the metadata for the given entity id in the given set. It will throw an diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php index c80e6607290f4c7891786b19483c33dd86d30afe..ef6371b9d5f6c47cdabc47faaeff9b8927710909 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php @@ -91,6 +91,37 @@ abstract class SimpleSAML_Metadata_MetaDataStorageSource { /* No entries matched - we should return NULL. */ return NULL; } + + /** + * This function will go through all the metadata, and check the hint.cidr + * parameter, which defines a network space (ip range) for each remote entry. + * This function returns the entityID for any of the entities that have an + * IP range which the IP falls within. + * + * @param $set Which set of metadata we are looking it up in. + * @param $ip IP address + * @return The entity id of a entity which have a CIDR hint where the provided + * IP address match. + */ + public function getPreferredEntityIdFromCIDRhint($set, $ip) { + + $metadataSet = $this->getMetadataSet($set); + + foreach($metadataSet AS $entityId => $entry) { + + if(!array_key_exists('hint.cidr', $entry)) continue; + if(!is_array($entry['hint.cidr'])) continue; + + foreach ($entry['hint.cidr'] AS $hint_entry) { + if (ipCIDRcheck($hint_entry, $ip)) + return $entityId; + } + + } + + /* No entries matched - we should return NULL. */ + return NULL; + } /** diff --git a/lib/SimpleSAML/Utilities.php b/lib/SimpleSAML/Utilities.php index 91d6ce1eba8eee88933740b501865d055620f9c0..1aff02c74cf2321e971150fdca65998b4d793167 100644 --- a/lib/SimpleSAML/Utilities.php +++ b/lib/SimpleSAML/Utilities.php @@ -365,6 +365,23 @@ class SimpleSAML_Utilities { exit; } + + /** + * Check whether an IP address is part of an CIDR. + */ + static function ipCIDRcheck($cidr, $ip = null) { + if ($ip == null) $ip = $_SERVER['REMOTE_ADDR']; + list ($net, $mask) = split ("/", $cidr); + + $ip_net = ip2long ($net); + $ip_mask = ~((1 << (32 - $mask)) - 1); + + $ip_ip = ip2long ($ip); + + $ip_ip_net = $ip_ip & $ip_mask; + + return ($ip_ip_net == $ip_net); + } /* This function redirects the user to the specified address. diff --git a/metadata-templates/saml20-idp-remote.php b/metadata-templates/saml20-idp-remote.php index 63dcb368738ea25acb4f8513619c709d52c0b2ee..34f3c51e1811bd0abc1d5cb8f91662e45f8d4c39 100644 --- a/metadata-templates/saml20-idp-remote.php +++ b/metadata-templates/saml20-idp-remote.php @@ -76,7 +76,8 @@ $metadata = array( 'SingleSignOnService' => 'https://max.feide.no/amserver/SSORedirect/metaAlias/idp', 'SingleLogoutService' => 'https://max.feide.no/amserver/IDPSloRedirect/metaAlias/idp', 'certFingerprint' => '3fa158e8abfd4b5203315b08c0b791b6ee4715f6', - 'base64attributes' => true + 'base64attributes' => true, + 'hint.cidr' => '158.38.0.0/16' ), /* @@ -88,7 +89,8 @@ $metadata = array( 'SingleSignOnService' => 'https://sam.feide.no/amserver/SSORedirect/metaAlias/idp', 'SingleLogoutService' => 'https://sam.feide.no/amserver/IDPSloRedirect/metaAlias/idp', 'certFingerprint' => '3a:e7:d3:d3:06:ba:57:fd:7f:62:6a:4b:a8:64:b3:4a:53:d9:5d:d0', - 'base64attributes' => true + 'base64attributes' => true, + 'hint.cidr' => '158.38.0.0/16' ) ); diff --git a/templates/default/en/selectidp-dropdown.php b/templates/default/en/selectidp-dropdown.php index 51964b36e5ccd93d29068450a7ca2694e9ac3076..3ba59d5bbccf0ad603b23cbfdbe5c5de8c85ed18 100644 --- a/templates/default/en/selectidp-dropdown.php +++ b/templates/default/en/selectidp-dropdown.php @@ -7,18 +7,21 @@ <h2><?php if (isset($data['header'])) { echo $data['header']; } else { echo "Select your IdP"; } ?></h2> <p>Please select the identity provider where you want to authenticate:</p> - + <form method="get" action="<?php echo $data['urlpattern']; ?>"> <input type="hidden" name="entityID" value="<?php echo htmlspecialchars($data['entityID']); ?>" /> <input type="hidden" name="return" value="<?php echo htmlspecialchars($data['return']); ?>" /> <input type="hidden" name="returnIDParam" value="<?php echo htmlspecialchars($data['returnIDParam']); ?>" /> <select name="idpentityid"> <?php - + foreach ($data['idplist'] AS $idpentry) { echo '<option value="'.htmlspecialchars($idpentry['entityid']).'"'; - if ($idpentry['entityid'] == $data['preferedidp']) echo ' selected="selected"'; + if (isset($this->data['preferredidp']) && + $idpentry['entityid'] == $this->data['preferredidp']) + echo ' selected="selected"'; + echo '>'.htmlspecialchars($idpentry['name']).'</option>'; } diff --git a/templates/default/en/selectidp-links.php b/templates/default/en/selectidp-links.php index 7d5cf1ec5cfd7c04019bf9de1796f9ed4a0715ae..daab979264755e1959809275244feb60702bd1c9 100644 --- a/templates/default/en/selectidp-links.php +++ b/templates/default/en/selectidp-links.php @@ -9,13 +9,25 @@ <?php + - foreach ($data['idplist'] AS $idpentry) { - - echo '<h3>' . htmlspecialchars($idpentry['name']) . '</h3>'; - echo '<p>' . htmlspecialchars($idpentry['description']) . '<br />'; - echo '[ <a href="' . $data['urlpattern'] . htmlspecialchars($idpentry['entityid']) . '">Select this IdP</a>]</p>'; + if (!empty($this->data['preferredidp']) && array_key_exists($this->data['preferredidp'], $this->data['idplist'])) { + $idpentry = $this->data['idplist'][$this->data['preferredidp']]; + echo '<div class="preferredidp">'; + echo ' <img src="/' . $this->data['baseurlpath'] .'resources/icons/star.png" style="float: right" />'; + echo ' <h3>' . htmlspecialchars($idpentry['name']) . '</h3>'; + echo ' <p>' . htmlspecialchars($idpentry['description']) . '<br />'; + echo ' [ <a href="' . $data['urlpattern'] . htmlspecialchars($idpentry['entityid']) . '">Select this IdP</a>]</p>'; + echo '</div>'; + } + + foreach ($data['idplist'] AS $idpentry) { + if ($idpentry['entityid'] != $this->data['preferredidp']) { + echo '<h3>' . htmlspecialchars($idpentry['name']) . '</h3>'; + echo '<p>' . htmlspecialchars($idpentry['description']) . '<br />'; + echo '[ <a href="' . $data['urlpattern'] . htmlspecialchars($idpentry['entityid']) . '">Select this IdP</a>]</p>'; + } } ?> diff --git a/www/admin/metadata.php b/www/admin/metadata.php index a40491f2cb63786d3f2139f6379f428d87edebc7..ce5ad8e890c67468e9e8104e7cd6da1214beb640 100644 --- a/www/admin/metadata.php +++ b/www/admin/metadata.php @@ -44,7 +44,7 @@ try { foreach ($metalist AS $entityid => $mentry) { $results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry, array('entityid', 'SingleSignOnService', 'SingleLogoutService', 'certFingerprint'), - array('name', 'description', 'base64attributes','request.signing','certificate') + array('name', 'description', 'base64attributes','request.signing','certificate', 'hint.cidr') ); } $et->data['metadata.saml20-idp-remote'] = $results; diff --git a/www/resources/default.css b/www/resources/default.css index 770f3c60c0398d7c937977629b692fffd39e962c..2ef216c163dffa274f44ba5169092b99bfc58a49 100644 --- a/www/resources/default.css +++ b/www/resources/default.css @@ -244,4 +244,8 @@ th.rowtitle { border: 1px solid #eee; padding: 2px; } - +div.preferredidp { + border: 1px dashed #ccc; + background: #eee; + padding: 2px 2em 2px 2em; +} \ No newline at end of file diff --git a/www/resources/icons/star.png b/www/resources/icons/star.png new file mode 100644 index 0000000000000000000000000000000000000000..2713202e0d6e2263c81149e779902ff943c9ce67 Binary files /dev/null and b/www/resources/icons/star.png differ diff --git a/www/saml2/sp/idpdisco.php b/www/saml2/sp/idpdisco.php index ce16c0bfea5deb6d1c6da4de04e4806f19ae7b60..b6cc018fc2765b1b5c8ffa897b8050dab09ad460 100644 --- a/www/saml2/sp/idpdisco.php +++ b/www/saml2/sp/idpdisco.php @@ -2,7 +2,6 @@ require_once('../../_include.php'); - require_once('SimpleSAML/Utilities.php'); require_once('SimpleSAML/Session.php'); require_once('SimpleSAML/XHTML/Template.php'); @@ -11,10 +10,8 @@ require_once('SimpleSAML/Metadata/MetaDataStorageHandler.php'); $config = SimpleSAML_Configuration::getInstance(); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); - $session = SimpleSAML_Session::getInstance(); - SimpleSAML_Logger::info('SAML2.0 - SP.idpDisco: Accessing SAML 2.0 discovery service'); if (!$config->getValue('enable.saml20-sp', false)) @@ -50,26 +47,39 @@ if (isset($_GET['idpentityid'])) { try { $idplist = $metadata->getList('saml20-idp-remote'); + $preferredidp = $metadata->getPreferredEntityIdFromCIDRhint('saml20-idp-remote', $_SERVER['REMOTE_ADDR']); + + if (!empty($preferredidp)) + SimpleSAML_Logger::info('SAML2.0 - SP.idpDisco: Preferred IdP from CIDR hint [ ' . $preferredidp . '].'); + } catch (Exception $exception) { SimpleSAML_Utilities::fatalError($session->getTrackID(), 'METADATA', $exception); } +if (!empty($_COOKIE['preferedidp'])) { + $preferredidp = $_COOKIE['preferedidp']; + SimpleSAML_Logger::info('SAML2.0 - SP.idpDisco: Preferred IdP overridden from cookie [ ' . $preferredidp . '].'); +} + + +/** + * Make use of an XHTML template to present the select IdP choice to the user. + * Currently the supported options is either a drop down menu or a list view. + */ +$templatefile = ($config->getValue('idpdisco.layout') == 'dropdown' ? 'selectidp-dropdown.php' : 'selectidp-links.php'); +$t = new SimpleSAML_XHTML_Template($config, $templatefile); +$t->data['header'] = 'Select your identity provider'; +$t->data['idplist'] = $idplist; +$t->data['preferredidp'] = $preferredidp; + if ($config->getValue('idpdisco.layout') == 'dropdown') { - $t = new SimpleSAML_XHTML_Template($config, 'selectidp-dropdown.php'); - $t->data['header'] = 'Select your identity provider'; - $t->data['idplist'] = $idplist; $t->data['return']= $return; $t->data['returnIDParam'] = $returnidparam; $t->data['entityID'] = $spentityid; - $t->data['preferedidp'] = (!empty($_COOKIE['preferedidp'])) ? $_COOKIE['preferedidp'] : null; $t->data['urlpattern'] = htmlentities(SimpleSAML_Utilities::selfURLNoQuery()); $t->show(); -} -else -{ - $t = new SimpleSAML_XHTML_Template($config, 'selectidp-links.php'); - $t->data['header'] = 'Select your identity provider'; - $t->data['idplist'] = $idplist; + +} else { $t->data['urlpattern'] = htmlentities(SimpleSAML_Utilities::selfURL() . '&idpentityid='); $t->show(); }