1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /** 6 * Licensed to the Apache Software Foundation (ASF) under one 7 * or more contributor license agreements. See the NOTICE file 8 * distributed with this work for additional information 9 * regarding copyright ownership. The ASF licenses this file 10 * to you under the Apache License, Version 2.0 (the 11 * "License"); you may not use this file except in compliance 12 * with the License. You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, 17 * software distributed under the License is distributed on an 18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 * KIND, either express or implied. See the License for the 20 * specific language governing permissions and limitations 21 * under the License. 22 */ 23 package com.sun.org.apache.xml.internal.security.utils; 24 25 import java.math.BigInteger; 26 import java.util.concurrent.ConcurrentHashMap; 27 import java.util.Map; 28 29 import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; 30 import org.w3c.dom.Attr; 31 import org.w3c.dom.Document; 32 import org.w3c.dom.Element; 33 import org.w3c.dom.Node; 34 import org.w3c.dom.NodeList; 35 import org.w3c.dom.Text; 36 37 /** 38 * This is the base class to all Objects which have a direct 1:1 mapping to an 39 * Element in a particular namespace. 40 */ 41 public abstract class ElementProxy { 42 43 protected static final com.sun.org.slf4j.internal.Logger LOG = 44 com.sun.org.slf4j.internal.LoggerFactory.getLogger(ElementProxy.class); 45 46 /** 47 * What XML element does this ElementProxy instance wrap? 48 */ 49 private Element wrappedElement; 50 51 /** Field baseURI */ 52 protected String baseURI; 53 54 /** Field doc */ 55 private Document wrappedDoc; 56 57 /** Field prefixMappings */ 58 private static Map<String, String> prefixMappings = new ConcurrentHashMap<>(); 59 60 /** 61 * Constructor ElementProxy 62 * 63 */ ElementProxy()64 public ElementProxy() { 65 } 66 67 /** 68 * Constructor ElementProxy 69 * 70 * @param doc 71 */ ElementProxy(Document doc)72 public ElementProxy(Document doc) { 73 if (doc == null) { 74 throw new RuntimeException("Document is null"); 75 } 76 77 this.wrappedDoc = doc; 78 this.wrappedElement = createElementForFamilyLocal(this.getBaseNamespace(), this.getBaseLocalName()); 79 } 80 81 /** 82 * Constructor ElementProxy 83 * 84 * @param element 85 * @param baseURI 86 * @throws XMLSecurityException 87 */ ElementProxy(Element element, String baseURI)88 public ElementProxy(Element element, String baseURI) throws XMLSecurityException { 89 if (element == null) { 90 throw new XMLSecurityException("ElementProxy.nullElement"); 91 } 92 93 LOG.debug("setElement(\"{}\", \"{}\")", element.getTagName(), baseURI); 94 95 setElement(element); 96 this.baseURI = baseURI; 97 98 this.guaranteeThatElementInCorrectSpace(); 99 } 100 101 /** 102 * Returns the namespace of the Elements of the sub-class. 103 * 104 * @return the namespace of the Elements of the sub-class. 105 */ getBaseNamespace()106 public abstract String getBaseNamespace(); 107 108 /** 109 * Returns the localname of the Elements of the sub-class. 110 * 111 * @return the localname of the Elements of the sub-class. 112 */ getBaseLocalName()113 public abstract String getBaseLocalName(); 114 115 createElementForFamilyLocal( String namespace, String localName )116 protected Element createElementForFamilyLocal( 117 String namespace, String localName 118 ) { 119 Document doc = getDocument(); 120 Element result = null; 121 if (namespace == null) { 122 result = doc.createElementNS(null, localName); 123 } else { 124 String baseName = this.getBaseNamespace(); 125 String prefix = ElementProxy.getDefaultPrefix(baseName); 126 if (prefix == null || prefix.length() == 0) { 127 result = doc.createElementNS(namespace, localName); 128 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", namespace); 129 } else { 130 result = doc.createElementNS(namespace, prefix + ":" + localName); 131 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix, namespace); 132 } 133 } 134 return result; 135 } 136 137 138 /** 139 * This method creates an Element in a given namespace with a given localname. 140 * It uses the {@link ElementProxy#getDefaultPrefix} method to decide whether 141 * a particular prefix is bound to that namespace. 142 * <p></p> 143 * This method was refactored out of the constructor. 144 * 145 * @param doc 146 * @param namespace 147 * @param localName 148 * @return The element created. 149 */ createElementForFamily(Document doc, String namespace, String localName)150 public static Element createElementForFamily(Document doc, String namespace, String localName) { 151 Element result = null; 152 String prefix = ElementProxy.getDefaultPrefix(namespace); 153 154 if (namespace == null) { 155 result = doc.createElementNS(null, localName); 156 } else { 157 if (prefix == null || prefix.length() == 0) { 158 result = doc.createElementNS(namespace, localName); 159 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", namespace); 160 } else { 161 result = doc.createElementNS(namespace, prefix + ":" + localName); 162 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix, namespace); 163 } 164 } 165 166 return result; 167 } 168 169 /** 170 * Method setElement 171 * 172 * @param element 173 * @param baseURI 174 * @throws XMLSecurityException 175 */ setElement(Element element, String baseURI)176 public void setElement(Element element, String baseURI) throws XMLSecurityException { 177 if (element == null) { 178 throw new XMLSecurityException("ElementProxy.nullElement"); 179 } 180 181 LOG.debug("setElement({}, \"{}\")", element.getTagName(), baseURI); 182 183 setElement(element); 184 this.baseURI = baseURI; 185 } 186 187 /** 188 * Returns the Element which was constructed by the Object. 189 * 190 * @return the Element which was constructed by the Object. 191 */ getElement()192 public final Element getElement() { 193 return this.wrappedElement; 194 } 195 196 /** 197 * Returns the Element plus a leading and a trailing CarriageReturn Text node. 198 * 199 * @return the Element which was constructed by the Object. 200 */ getElementPlusReturns()201 public final NodeList getElementPlusReturns() { 202 203 HelperNodeList nl = new HelperNodeList(); 204 205 nl.appendChild(createText("\n")); 206 nl.appendChild(getElement()); 207 nl.appendChild(createText("\n")); 208 209 return nl; 210 } 211 createText(String text)212 protected Text createText(String text) { 213 return getDocument().createTextNode(text); 214 } 215 216 /** 217 * Method getDocument 218 * 219 * @return the Document where this element is contained. 220 */ getDocument()221 public Document getDocument() { 222 if (wrappedDoc == null) { 223 wrappedDoc = XMLUtils.getOwnerDocument(wrappedElement); 224 } 225 return wrappedDoc; 226 } 227 228 /** 229 * Method getBaseURI 230 * 231 * @return the base uri of the namespace of this element 232 */ getBaseURI()233 public String getBaseURI() { 234 return this.baseURI; 235 } 236 237 /** 238 * Method guaranteeThatElementInCorrectSpace 239 * 240 * @throws XMLSecurityException 241 */ guaranteeThatElementInCorrectSpace()242 void guaranteeThatElementInCorrectSpace() throws XMLSecurityException { 243 244 String expectedLocalName = this.getBaseLocalName(); 245 String expectedNamespaceUri = this.getBaseNamespace(); 246 247 String actualLocalName = getElement().getLocalName(); 248 String actualNamespaceUri = getElement().getNamespaceURI(); 249 250 if(!expectedNamespaceUri.equals(actualNamespaceUri) 251 && !expectedLocalName.equals(actualLocalName)) { 252 Object[] exArgs = { actualNamespaceUri + ":" + actualLocalName, 253 expectedNamespaceUri + ":" + expectedLocalName}; 254 throw new XMLSecurityException("xml.WrongElement", exArgs); 255 } 256 } 257 258 /** 259 * Method addBigIntegerElement 260 * 261 * @param bi 262 * @param localname 263 */ addBigIntegerElement(BigInteger bi, String localname)264 public void addBigIntegerElement(BigInteger bi, String localname) { 265 if (bi != null) { 266 Element e = XMLUtils.createElementInSignatureSpace(getDocument(), localname); 267 268 byte[] bytes = XMLUtils.getBytes(bi, bi.bitLength()); 269 String encodedInt = XMLUtils.encodeToString(bytes); 270 271 Document doc = e.getOwnerDocument(); 272 Text text = doc.createTextNode(encodedInt); 273 274 e.appendChild(text); 275 276 appendSelf(e); 277 addReturnToSelf(); 278 } 279 } 280 addReturnToSelf()281 protected void addReturnToSelf() { 282 XMLUtils.addReturnToElement(getElement()); 283 } 284 285 /** 286 * Method addBase64Element 287 * 288 * @param bytes 289 * @param localname 290 */ addBase64Element(byte[] bytes, String localname)291 public void addBase64Element(byte[] bytes, String localname) { 292 if (bytes != null) { 293 addTextElement(XMLUtils.encodeToString(bytes), localname); 294 } 295 } 296 297 /** 298 * Method addTextElement 299 * 300 * @param text 301 * @param localname 302 */ addTextElement(String text, String localname)303 public void addTextElement(String text, String localname) { 304 Element e = XMLUtils.createElementInSignatureSpace(getDocument(), localname); 305 Text t = createText(text); 306 307 appendOther(e, t); 308 appendSelf(e); 309 addReturnToSelf(); 310 } 311 312 /** 313 * Method addBase64Text 314 * 315 * @param bytes 316 */ addBase64Text(byte[] bytes)317 public void addBase64Text(byte[] bytes) { 318 if (bytes != null) { 319 Text t = XMLUtils.ignoreLineBreaks() 320 ? createText(XMLUtils.encodeToString(bytes)) 321 : createText("\n" + XMLUtils.encodeToString(bytes) + "\n"); 322 appendSelf(t); 323 } 324 } 325 appendSelf(ElementProxy toAppend)326 protected void appendSelf(ElementProxy toAppend) { 327 getElement().appendChild(toAppend.getElement()); 328 } 329 appendSelf(Node toAppend)330 protected void appendSelf(Node toAppend) { 331 getElement().appendChild(toAppend); 332 } 333 appendOther(Element parent, Node toAppend)334 protected void appendOther(Element parent, Node toAppend) { 335 parent.appendChild(toAppend); 336 } 337 338 /** 339 * Method addText 340 * 341 * @param text 342 */ addText(String text)343 public void addText(String text) { 344 if (text != null) { 345 Text t = createText(text); 346 347 appendSelf(t); 348 } 349 } 350 351 /** 352 * Method getVal 353 * 354 * @param localname 355 * @param namespace 356 * @return The biginteger contained in the given element 357 */ getBigIntegerFromChildElement( String localname, String namespace )358 public BigInteger getBigIntegerFromChildElement( 359 String localname, String namespace 360 ) { 361 Node n = XMLUtils.selectNode(getFirstChild(), namespace, localname, 0); 362 if (n != null) { 363 return new BigInteger(1, XMLUtils.decode(XMLUtils.getFullTextChildrenFromNode(n))); 364 } 365 return null; 366 } 367 368 /** 369 * Method getTextFromChildElement 370 * 371 * @param localname 372 * @param namespace 373 * @return the Text of the textNode 374 */ getTextFromChildElement(String localname, String namespace)375 public String getTextFromChildElement(String localname, String namespace) { 376 return XMLUtils.selectNode( 377 getFirstChild(), 378 namespace, 379 localname, 380 0).getTextContent(); 381 } 382 383 /** 384 * Method getBytesFromTextChild 385 * 386 * @return The base64 bytes from the text children of this element 387 * @throws XMLSecurityException 388 */ getBytesFromTextChild()389 public byte[] getBytesFromTextChild() throws XMLSecurityException { 390 return XMLUtils.decode(getTextFromTextChild()); 391 } 392 393 /** 394 * Method getTextFromTextChild 395 * 396 * @return the Text obtained by concatenating all the text nodes of this 397 * element 398 */ getTextFromTextChild()399 public String getTextFromTextChild() { 400 return XMLUtils.getFullTextChildrenFromNode(getElement()); 401 } 402 403 /** 404 * Method length 405 * 406 * @param namespace 407 * @param localname 408 * @return the number of elements {namespace}:localname under this element 409 */ length(String namespace, String localname)410 public int length(String namespace, String localname) { 411 int number = 0; 412 Node sibling = getFirstChild(); 413 while (sibling != null) { 414 if (localname.equals(sibling.getLocalName()) 415 && namespace.equals(sibling.getNamespaceURI())) { 416 number++; 417 } 418 sibling = sibling.getNextSibling(); 419 } 420 return number; 421 } 422 423 /** 424 * Adds an xmlns: definition to the Element. This can be called as follows: 425 * 426 * <PRE> 427 * // set namespace with ds prefix 428 * xpathContainer.setXPathNamespaceContext("ds", "http://www.w3.org/2000/09/xmldsig#"); 429 * xpathContainer.setXPathNamespaceContext("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#"); 430 * </PRE> 431 * 432 * @param prefix 433 * @param uri 434 * @throws XMLSecurityException 435 */ setXPathNamespaceContext(String prefix, String uri)436 public void setXPathNamespaceContext(String prefix, String uri) 437 throws XMLSecurityException { 438 String ns; 439 440 if (prefix == null || prefix.length() == 0) { 441 throw new XMLSecurityException("defaultNamespaceCannotBeSetHere"); 442 } else if ("xmlns".equals(prefix)) { 443 throw new XMLSecurityException("defaultNamespaceCannotBeSetHere"); 444 } else if (prefix.startsWith("xmlns:")) { 445 ns = prefix;//"xmlns:" + prefix.substring("xmlns:".length()); 446 } else { 447 ns = "xmlns:" + prefix; 448 } 449 450 Attr a = getElement().getAttributeNodeNS(Constants.NamespaceSpecNS, ns); 451 452 if (a != null) { 453 if (!a.getNodeValue().equals(uri)) { 454 Object[] exArgs = { ns, getElement().getAttributeNS(null, ns) }; 455 456 throw new XMLSecurityException("namespacePrefixAlreadyUsedByOtherURI", exArgs); 457 } 458 return; 459 } 460 461 getElement().setAttributeNS(Constants.NamespaceSpecNS, ns, uri); 462 } 463 464 /** 465 * Method setDefaultPrefix 466 * 467 * @param namespace 468 * @param prefix 469 * @throws XMLSecurityException 470 * @throws SecurityException if a security manager is installed and the 471 * caller does not have permission to set the default prefix 472 */ setDefaultPrefix(String namespace, String prefix)473 public static void setDefaultPrefix(String namespace, String prefix) 474 throws XMLSecurityException { 475 JavaUtils.checkRegisterPermission(); 476 setNamespacePrefix(namespace, prefix); 477 } 478 setNamespacePrefix(String namespace, String prefix)479 private static void setNamespacePrefix(String namespace, String prefix) 480 throws XMLSecurityException { 481 if (prefixMappings.containsValue(prefix)) { 482 String storedPrefix = prefixMappings.get(namespace); 483 if (!storedPrefix.equals(prefix)) { 484 Object[] exArgs = { prefix, namespace, storedPrefix }; 485 486 throw new XMLSecurityException("prefix.AlreadyAssigned", exArgs); 487 } 488 } 489 490 if (Constants.SignatureSpecNS.equals(namespace)) { 491 XMLUtils.setDsPrefix(prefix); 492 } else if (Constants.SignatureSpec11NS.equals(namespace)) { 493 XMLUtils.setDs11Prefix(prefix); 494 } else if (EncryptionConstants.EncryptionSpecNS.equals(namespace)) { 495 XMLUtils.setXencPrefix(prefix); 496 } 497 prefixMappings.put(namespace, prefix); 498 } 499 500 /** 501 * This method registers the default prefixes. 502 */ registerDefaultPrefixes()503 public static void registerDefaultPrefixes() throws XMLSecurityException { 504 setNamespacePrefix("http://www.w3.org/2000/09/xmldsig#", "ds"); 505 setNamespacePrefix("http://www.w3.org/2001/04/xmlenc#", "xenc"); 506 setNamespacePrefix("http://www.w3.org/2009/xmlenc11#", "xenc11"); 507 setNamespacePrefix("http://www.xmlsecurity.org/experimental#", "experimental"); 508 setNamespacePrefix("http://www.w3.org/2002/04/xmldsig-filter2", "dsig-xpath-old"); 509 setNamespacePrefix("http://www.w3.org/2002/06/xmldsig-filter2", "dsig-xpath"); 510 setNamespacePrefix("http://www.w3.org/2001/10/xml-exc-c14n#", "ec"); 511 setNamespacePrefix( 512 "http://www.nue.et-inf.uni-siegen.de/~geuer-pollmann/#xpathFilter", "xx" 513 ); 514 setNamespacePrefix("http://www.w3.org/2009/xmldsig11#", "dsig11"); 515 } 516 517 /** 518 * Method getDefaultPrefix 519 * 520 * @param namespace 521 * @return the default prefix bind to this element. 522 */ getDefaultPrefix(String namespace)523 public static String getDefaultPrefix(String namespace) { 524 return prefixMappings.get(namespace); 525 } 526 527 /** 528 * New value for the wrapped XML element that this object is a proxy for. 529 * 530 * @param elem New element 531 * 532 * @see #getElement() 533 */ setElement(Element elem)534 protected void setElement(Element elem) { 535 wrappedElement = elem; 536 } 537 538 /** 539 * Set a new value for the wrapped document that this object is a proxy for. 540 * 541 * @param doc New document object being wrapped. 542 * 543 * @see #getDocument() 544 */ setDocument(Document doc)545 protected void setDocument(Document doc) { 546 wrappedDoc = doc; 547 } 548 getLocalAttribute(String attrName)549 protected String getLocalAttribute(String attrName) { 550 return getElement().getAttributeNS(null, attrName); 551 } 552 setLocalAttribute(String attrName, String value)553 protected void setLocalAttribute(String attrName, String value) { 554 getElement().setAttributeNS(null, attrName, value); 555 } 556 setLocalIdAttribute(String attrName, String value)557 protected void setLocalIdAttribute(String attrName, String value) { 558 559 if (value != null) { 560 Attr attr = getDocument().createAttributeNS(null, attrName); 561 attr.setValue(value); 562 getElement().setAttributeNodeNS(attr); 563 getElement().setIdAttributeNode(attr, true); 564 } 565 else { 566 getElement().removeAttributeNS(null, attrName); 567 } 568 } 569 getFirstChild()570 protected Node getFirstChild() { 571 return getElement().getFirstChild(); 572 } 573 574 } 575