Skip to content

Commit 428ffa8

Browse files
committed
Fix #283.New method of importing a decrypted assertion into the XML document to replace the EncryptedAssertion. Fix signature issues on Signed Encrypted Assertions with default namespace
1 parent 12948aa commit 428ffa8

2 files changed

Lines changed: 63 additions & 20 deletions

File tree

lib/Saml2/Response.php

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ public function getAttributesWithFriendlyName()
720720
return $this->_getAttributesByKeyName('FriendlyName');
721721
}
722722

723-
private function _getAttributesByKeyName($keyName="Name")
723+
private function _getAttributesByKeyName($keyName = "Name")
724724
{
725725
$attributes = array();
726726

@@ -1028,14 +1028,12 @@ private function _query($query)
10281028
protected function _decryptAssertion($dom)
10291029
{
10301030
$pem = $this->_settings->getSPkey();
1031-
10321031
if (empty($pem)) {
10331032
throw new OneLogin_Saml2_Error(
10341033
"No private key available, check settings",
10351034
OneLogin_Saml2_Error::PRIVATE_KEY_NOT_FOUND
10361035
);
10371036
}
1038-
10391037
$objenc = new XMLSecEnc();
10401038
$encData = $objenc->locateEncryptedData($dom);
10411039
if (!$encData) {
@@ -1044,7 +1042,6 @@ protected function _decryptAssertion($dom)
10441042
OneLogin_Saml2_ValidationError::MISSING_ENCRYPTED_ELEMENT
10451043
);
10461044
}
1047-
10481045
$objenc->setNode($encData);
10491046
$objenc->type = $encData->getAttribute("Type");
10501047
if (!$objKey = $objenc->locateKey()) {
@@ -1053,7 +1050,6 @@ protected function _decryptAssertion($dom)
10531050
OneLogin_Saml2_ValidationError::KEY_ALGORITHM_ERROR
10541051
);
10551052
}
1056-
10571053
$key = null;
10581054
if ($objKeyInfo = $objenc->locateKeyInfo($objKey)) {
10591055
if ($objKeyInfo->isEncrypted) {
@@ -1069,36 +1065,41 @@ protected function _decryptAssertion($dom)
10691065
if (empty($objKey->key)) {
10701066
$objKey->loadKey($key);
10711067
}
1072-
1073-
$decrypted = $objenc->decryptNode($objKey, true);
1074-
1075-
if ($decrypted instanceof DOMDocument) {
1068+
$decryptedXML = $objenc->decryptNode($objKey, false);
1069+
$decrypted = new DOMDocument();
1070+
$check = OneLogin_Saml2_Utils::loadXML($decrypted, $decryptedXML);
1071+
if ($check === false) {
1072+
throw new Exception('Error: string from decrypted assertion could not be loaded into a XML document');
1073+
}
1074+
if ($encData->parentNode instanceof DOMDocument) {
10761075
return $decrypted;
10771076
} else {
1078-
$encryptedAssertion = $decrypted->parentNode;
1077+
$decrypted = $decrypted->documentElement;
1078+
$encryptedAssertion = $encData->parentNode;
10791079
$container = $encryptedAssertion->parentNode;
10801080

1081-
# Fix possible issue with saml namespace
1082-
if (!$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml') &&
1083-
!$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml2') &&
1084-
!$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns') &&
1085-
!$container->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml') &&
1086-
!$container->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml2')
1087-
) {
1081+
// Fix possible issue with saml namespace
1082+
if (!$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml')
1083+
&& !$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml2')
1084+
&& !$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns')
1085+
&& !$container->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml')
1086+
&& !$container->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml2')
1087+
) {
10881088
if (strpos($encryptedAssertion->tagName, 'saml2:') !== false) {
10891089
$ns = 'xmlns:saml2';
10901090
} else if (strpos($encryptedAssertion->tagName, 'saml:') !== false) {
10911091
$ns = 'xmlns:saml';
10921092
} else {
10931093
$ns = 'xmlns';
10941094
}
1095-
10961095
$decrypted->setAttributeNS('http://www.w3.org/2000/xmlns/', $ns, OneLogin_Saml2_Constants::NS_SAML);
10971096
}
10981097

1099-
$container->replaceChild($decrypted, $encryptedAssertion);
1098+
OneLogin_Saml2_Utils::treeCopyReplace($encryptedAssertion, $decrypted);
11001099

1101-
return $decrypted->ownerDocument;
1100+
// Rebuild the DOM will fix issues with namespaces as well
1101+
$dom = new DOMDocument();
1102+
return OneLogin_Saml2_Utils::loadXML($dom, $container->ownerDocument->saveXML());
11021103
}
11031104
}
11041105

lib/Saml2/Utils.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,48 @@ public static function validateXML($xml, $schema, $debug = false)
145145
return $dom;
146146
}
147147

148+
/**
149+
* Import a node tree into a target document
150+
* Copy it before a reference node as a sibling
151+
* and at the end of the copy remove
152+
* the reference node in the target document
153+
* As it were 'replacing' it
154+
* Leaving nested default namespaces alone
155+
* (Standard importNode with deep copy
156+
* mangles nested default namespaces)
157+
*
158+
* The reference node must not be a DomDocument
159+
* It CAN be the top element of a document
160+
* Returns the copied node in the target document
161+
*
162+
* @param DomNode $targetNode
163+
* @param DomNode $sourceNode
164+
* @param bool $recurse
165+
* @return DOMNode
166+
* @throws Exception
167+
*/
168+
public static function treeCopyReplace(DomNode $targetNode, DomNode $sourceNode, $recurse = false)
169+
{
170+
if ($targetNode->parentNode === null) {
171+
throw new Exception('Illegal argument targetNode. It has no parentNode.');
172+
}
173+
$clonedNode = $targetNode->ownerDocument->importNode($sourceNode, false);
174+
if ($recurse) {
175+
$resultNode = $targetNode->appendChild($clonedNode);
176+
} else {
177+
$resultNode = $targetNode->parentNode->insertBefore($clonedNode, $targetNode);
178+
}
179+
if ($sourceNode->childNodes !== null) {
180+
foreach ($sourceNode->childNodes as $child) {
181+
self::treeCopyReplace($resultNode, $child, true);
182+
}
183+
}
184+
if (!$recurse) {
185+
$targetNode->parentNode->removeChild($targetNode);
186+
}
187+
return $resultNode;
188+
}
189+
148190
/**
149191
* Returns a x509 cert (adding header & footer if required).
150192
*

0 commit comments

Comments
 (0)