|
11 | 11 | import it.pagopa.oneid.exception.GenericAuthnRequestCreationException; |
12 | 12 | import it.pagopa.oneid.exception.SAMLValidationException; |
13 | 13 | import it.pagopa.oneid.model.dto.AttributeDTO; |
| 14 | +import it.pagopa.oneid.service.config.SAMLNamespaceContext; |
14 | 15 | import it.pagopa.oneid.web.controller.interceptors.CurrentAuthDTO; |
15 | 16 | import jakarta.enterprise.context.ApplicationScoped; |
16 | 17 | import jakarta.inject.Inject; |
|
32 | 33 | import javax.xml.parsers.DocumentBuilder; |
33 | 34 | import javax.xml.parsers.DocumentBuilderFactory; |
34 | 35 | import javax.xml.parsers.ParserConfigurationException; |
| 36 | +import javax.xml.xpath.XPath; |
| 37 | +import javax.xml.xpath.XPathConstants; |
| 38 | +import javax.xml.xpath.XPathExpressionException; |
| 39 | +import javax.xml.xpath.XPathFactory; |
35 | 40 | import net.shibboleth.utilities.java.support.xml.BasicParserPool; |
36 | 41 | import net.shibboleth.utilities.java.support.xml.XMLParserException; |
37 | 42 | import org.eclipse.microprofile.config.inject.ConfigProperty; |
|
60 | 65 | import org.opensaml.xmlsec.signature.support.SignatureValidator; |
61 | 66 | import org.opensaml.xmlsec.signature.support.Signer; |
62 | 67 | import org.w3c.dom.Document; |
63 | | -import org.w3c.dom.Element; |
64 | | -import org.w3c.dom.Node; |
65 | | -import org.w3c.dom.NodeList; |
66 | 68 | import org.xml.sax.SAXException; |
67 | 69 |
|
68 | 70 | @ApplicationScoped |
@@ -188,47 +190,44 @@ private byte[] decodeBase64(String SAMLResponse) throws OneIdentityException { |
188 | 190 | } |
189 | 191 |
|
190 | 192 | private Response unmarshallResponse(byte[] decodedSamlResponse) throws OneIdentityException { |
| 193 | + // log size of SAMLResponse for possible future check on max size |
| 194 | + Log.info("SAMLResponse size: " + decodedSamlResponse.length); |
| 195 | + |
| 196 | + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
191 | 197 | try { |
192 | 198 |
|
193 | 199 | // Parse XML |
194 | | - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
| 200 | + dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); |
| 201 | + dbf.setXIncludeAware(false); |
195 | 202 | dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); |
196 | 203 | dbf.setNamespaceAware(true); |
| 204 | + |
197 | 205 | DocumentBuilder db = dbf.newDocumentBuilder(); |
198 | 206 | Document doc = db.parse(new ByteArrayInputStream(decodedSamlResponse)); |
199 | 207 |
|
200 | | - // Check Response doesn't have multiple Signatures |
201 | | - Element responseElem = doc.getDocumentElement(); |
202 | | - checkNoMultipleSignatures(responseElem); |
203 | | - // Check each Assertion doesn't have multiple Signatures |
204 | | - NodeList assertionNodes = responseElem.getElementsByTagNameNS( |
205 | | - "urn:oasis:names:tc:SAML:2.0:assertion", "Assertion"); |
206 | | - for (int i = 0; i < assertionNodes.getLength(); i++) { |
207 | | - checkNoMultipleSignatures((Element) assertionNodes.item(i)); |
208 | | - } |
| 208 | + checkNoMultipleSignatures(doc); |
209 | 209 |
|
210 | 210 | // return response even if it has multiple signatures, the error will be handled inside SAMLController |
211 | 211 | return (Response) XMLObjectSupport.unmarshallFromInputStream(basicParserPool, |
212 | 212 | new ByteArrayInputStream(decodedSamlResponse)); |
213 | 213 |
|
214 | 214 | } catch (UnmarshallingException | XMLParserException | SAXException | IOException | |
215 | | - ParserConfigurationException e) { |
| 215 | + ParserConfigurationException | XPathExpressionException e) { |
216 | 216 | Log.error("Unmarshalling error: " + e.getMessage()); |
217 | 217 | throw new OneIdentityException(e); |
218 | 218 | } |
219 | 219 | } |
220 | 220 |
|
221 | | - private void checkNoMultipleSignatures(Element elem) { |
222 | | - int count = 0; |
223 | | - NodeList children = elem.getChildNodes(); |
224 | | - for (int i = 0; i < children.getLength(); i++) { |
225 | | - Node n = children.item(i); |
226 | | - if (n.getNodeType() == Node.ELEMENT_NODE && "Signature".equals(n.getLocalName()) |
227 | | - && "http://www.w3.org/2000/09/xmldsig#".equals(n.getNamespaceURI())) { |
228 | | - count++; |
229 | | - } |
230 | | - } |
231 | | - if (count > 1) { |
| 221 | + private void checkNoMultipleSignatures(Document doc) throws XPathExpressionException { |
| 222 | + XPath xPath = XPathFactory.newInstance().newXPath(); |
| 223 | + // Set the namespace context to handle prefixes like 'samlp', 'saml', and 'ds' |
| 224 | + xPath.setNamespaceContext(new SAMLNamespaceContext()); |
| 225 | + |
| 226 | + String expression = "count(.//ds:Signature)"; |
| 227 | + Double responseSignatureCount = (Double) xPath.compile(expression) |
| 228 | + .evaluate(doc, XPathConstants.NUMBER); |
| 229 | + |
| 230 | + if (responseSignatureCount > 2) { |
232 | 231 | // set a flag in currentAuthDTO to handle the error in SAMLController |
233 | 232 | Log.error(ErrorCode.IDP_ERROR_MULTIPLE_SAMLRESPONSE_SIGNATURES_PRESENT.getErrorMessage()); |
234 | 233 | currentAuthDTO.setResponseWithMultipleSignatures(true); |
|
0 commit comments