logLoc = 'aggregator2:' . $aggregator->getId() . ': '; $this->aggregator = $aggregator; $this->url = $config->getString('url'); $this->sslCAFile = $config->getString('ssl.cafile', NULL); if ($this->sslCAFile === NULL) { $this->sslCAFile = $aggregator->getCAFile(); } $this->certificate = $config->getString('cert', NULL); $this->cacheId = sha1($this->url); $this->cacheTag = sha1(serialize($config)); } /** * Retrieve and parse the metadata. * * @return SAML2_XML_md_EntitiesDescriptor|SAML2_XML_md_EntityDescriptor|NULL * The downloaded metadata or NULL if we were unable to download or parse it. */ private function downloadMetadata() { SimpleSAML_Logger::debug($this->logLoc . 'Downloading metadata from ' . var_export($this->url, TRUE)); $context = array('ssl' => array()); if ($this->sslCAFile !== NULL) { $context['ssl']['cafile'] = SimpleSAML_Utilities::resolveCert($this->sslCAFile); SimpleSAML_Logger::debug($this->logLoc . 'Validating https connection against CA certificate(s) found in ' . var_export($context['ssl']['cafile'], TRUE)); $context['ssl']['verify_peer'] = TRUE; $context['ssl']['CN_match'] = parse_url($this->url, PHP_URL_HOST); } $data = SimpleSAML_Utilities::fetch($this->url, $context); if ($data === FALSE || $data === NULL) { SimpleSAML_Logger::error($this->logLoc . 'Unable to load metadata from ' . var_export($this->url, TRUE)); return NULL; } $doc = new DOMDocument(); $res = $doc->loadXML($data); if (!$res) { SimpleSAML_Logger::error($this->logLoc . 'Error parsing XML from ' . var_export($this->url, TRUE)); return NULL; } $root = SAML2_Utils::xpQuery($doc->firstChild, '/saml_metadata:EntityDescriptor|/saml_metadata:EntitiesDescriptor'); if (count($root) === 0) { SimpleSAML_Logger::error($this->logLoc . 'No or in metadata from ' . var_export($this->url, TRUE)); return NULL; } if (count($root) > 1) { SimpleSAML_Logger::error($this->logLoc . 'More than one or in metadata from ' . var_export($this->url, TRUE)); return NULL; } $root = $root[0]; try { if ($root->localName === 'EntityDescriptor') { $md = new SAML2_XML_md_EntityDescriptor($root); } else { $md = new SAML2_XML_md_EntitiesDescriptor($root); } } catch (Exception $e) { SimpleSAML_Logger::error($this->logLoc . 'Unable to parse metadata from ' . var_export($this->url, TRUE) . ': ' . $e->getMessage()); return NULL; } if ($this->certificate !== NULL) { $file = SimpleSAML_Utilities::resolveCert($this->certificate); $certData = file_get_contents($file); if ($certData === FALSE) { throw new SimpleSAML_Error_Exception('Error loading certificate from ' . var_export($file, TRUE)); } /* Extract the public key from the certificate for validation. */ $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public')); $key->loadKey($file, TRUE); if (!$md->validate($key)) { SimpleSAML_Logger::error($this->logLoc . 'Error validating signature on metadata.'); return NULL; } SimpleSAML_Logger::debug($this->logLoc . 'Validated signature on metadata from ' . var_export($this->url, TRUE)); } return $md; } /** * Attempt to update our cache file. */ public function updateCache() { if ($this->updateAttempted) { return; } $this->updateAttempted = TRUE; $this->metadata = $this->downloadMetadata(); if ($this->metadata === NULL) { return; } $expires = time() + 24*60*60; /* Default expires in one day. */ if ($this->metadata->validUntil !== NULL && $this->metadata->validUntil < $expires) { $expires = $this->metadata->validUntil; } $metadataSerialized = serialize($this->metadata); $this->aggregator->addCacheItem($this->cacheId, $metadataSerialized, $expires, $this->cacheTag); } /** * Retrieve the metadata file. * * This function will check its cached copy, to see whether it can be used. * * @return SAML2_XML_md_EntityDescriptor|SAML2_XML_md_EntitiesDescriptor|NULL The downloaded metadata. */ public function getMetadata() { if ($this->metadata !== NULL) { /* We have already downloaded the metdata. */ return $this->metadata; } if (!$this->aggregator->isCacheValid($this->cacheId, $this->cacheTag)) { $this->updateCache(); if ($this->metadata !== NULL) { return $this->metadata; } /* We were unable to update the cache - use cached metadata. */ } $cacheFile = $this->aggregator->getCacheFile($this->cacheId); if (!file_exists($cacheFile)) { SimpleSAML_Logger::error($this->logLoc . 'No cached metadata available.'); return NULL; } SimpleSAML_Logger::debug($this->logLoc . 'Using cached metadata from ' . var_export($cacheFile, TRUE)); $metadata = file_get_contents($cacheFile); if ($metadata !== NULL) { $this->metadata = unserialize($metadata); return $this->metadata; } return NULL; } }