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<String, String>(); 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 this.wrappedDoc.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 Element el = XMLUtils.createElementInSignatureSpace(getDocument(), localname); 294 Text text = getDocument().createTextNode(XMLUtils.encodeToString(bytes)); 295 296 el.appendChild(text); 297 298 appendSelf(el); 299 if (!XMLUtils.ignoreLineBreaks()) { 300 appendSelf(createText("\n")); 301 } 302 } 303 } 304 305 /** 306 * Method addTextElement 307 * 308 * @param text 309 * @param localname 310 */ addTextElement(String text, String localname)311 public void addTextElement(String text, String localname) { 312 Element e = XMLUtils.createElementInSignatureSpace(getDocument(), localname); 313 Text t = createText(text); 314 315 appendOther(e, t); 316 appendSelf(e); 317 addReturnToSelf(); 318 } 319 320 /** 321 * Method addBase64Text 322 * 323 * @param bytes 324 */ addBase64Text(byte[] bytes)325 public void addBase64Text(byte[] bytes) { 326 if (bytes != null) { 327 Text t = XMLUtils.ignoreLineBreaks() 328 ? createText(XMLUtils.encodeToString(bytes)) 329 : createText("\n" + XMLUtils.encodeToString(bytes) + "\n"); 330 appendSelf(t); 331 } 332 } 333 appendSelf(ElementProxy toAppend)334 protected void appendSelf(ElementProxy toAppend) { 335 getElement().appendChild(toAppend.getElement()); 336 } 337 appendSelf(Node toAppend)338 protected void appendSelf(Node toAppend) { 339 getElement().appendChild(toAppend); 340 } 341 appendOther(Element parent, Node toAppend)342 protected void appendOther(Element parent, Node toAppend) { 343 parent.appendChild(toAppend); 344 } 345 346 /** 347 * Method addText 348 * 349 * @param text 350 */ addText(String text)351 public void addText(String text) { 352 if (text != null) { 353 Text t = createText(text); 354 355 appendSelf(t); 356 } 357 } 358 359 /** 360 * Method getVal 361 * 362 * @param localname 363 * @param namespace 364 * @return The biginteger contained in the given element 365 */ getBigIntegerFromChildElement( String localname, String namespace )366 public BigInteger getBigIntegerFromChildElement( 367 String localname, String namespace 368 ) { 369 Node n = XMLUtils.selectNode(getFirstChild(), namespace, localname, 0); 370 if (n != null) { 371 return new BigInteger(1, XMLUtils.decode(XMLUtils.getFullTextChildrenFromNode(n))); 372 } 373 return null; 374 } 375 376 /** 377 * Method getTextFromChildElement 378 * 379 * @param localname 380 * @param namespace 381 * @return the Text of the textNode 382 */ getTextFromChildElement(String localname, String namespace)383 public String getTextFromChildElement(String localname, String namespace) { 384 return XMLUtils.selectNode( 385 getFirstChild(), 386 namespace, 387 localname, 388 0).getTextContent(); 389 } 390 391 /** 392 * Method getBytesFromTextChild 393 * 394 * @return The base64 bytes from the text children of this element 395 * @throws XMLSecurityException 396 */ getBytesFromTextChild()397 public byte[] getBytesFromTextChild() throws XMLSecurityException { 398 return XMLUtils.decode(getTextFromTextChild()); 399 } 400 401 /** 402 * Method getTextFromTextChild 403 * 404 * @return the Text obtained by concatenating all the text nodes of this 405 * element 406 */ getTextFromTextChild()407 public String getTextFromTextChild() { 408 return XMLUtils.getFullTextChildrenFromNode(getElement()); 409 } 410 411 /** 412 * Method length 413 * 414 * @param namespace 415 * @param localname 416 * @return the number of elements {namespace}:localname under this element 417 */ length(String namespace, String localname)418 public int length(String namespace, String localname) { 419 int number = 0; 420 Node sibling = getFirstChild(); 421 while (sibling != null) { 422 if (localname.equals(sibling.getLocalName()) 423 && namespace.equals(sibling.getNamespaceURI())) { 424 number++; 425 } 426 sibling = sibling.getNextSibling(); 427 } 428 return number; 429 } 430 431 /** 432 * Adds an xmlns: definition to the Element. This can be called as follows: 433 * 434 * <PRE> 435 * // set namespace with ds prefix 436 * xpathContainer.setXPathNamespaceContext("ds", "http://www.w3.org/2000/09/xmldsig#"); 437 * xpathContainer.setXPathNamespaceContext("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#"); 438 * </PRE> 439 * 440 * @param prefix 441 * @param uri 442 * @throws XMLSecurityException 443 */ setXPathNamespaceContext(String prefix, String uri)444 public void setXPathNamespaceContext(String prefix, String uri) 445 throws XMLSecurityException { 446 String ns; 447 448 if (prefix == null || prefix.length() == 0) { 449 throw new XMLSecurityException("defaultNamespaceCannotBeSetHere"); 450 } else if ("xmlns".equals(prefix)) { 451 throw new XMLSecurityException("defaultNamespaceCannotBeSetHere"); 452 } else if (prefix.startsWith("xmlns:")) { 453 ns = prefix;//"xmlns:" + prefix.substring("xmlns:".length()); 454 } else { 455 ns = "xmlns:" + prefix; 456 } 457 458 Attr a = getElement().getAttributeNodeNS(Constants.NamespaceSpecNS, ns); 459 460 if (a != null) { 461 if (!a.getNodeValue().equals(uri)) { 462 Object exArgs[] = { ns, getElement().getAttributeNS(null, ns) }; 463 464 throw new XMLSecurityException("namespacePrefixAlreadyUsedByOtherURI", exArgs); 465 } 466 return; 467 } 468 469 getElement().setAttributeNS(Constants.NamespaceSpecNS, ns, uri); 470 } 471 472 /** 473 * Method setDefaultPrefix 474 * 475 * @param namespace 476 * @param prefix 477 * @throws XMLSecurityException 478 * @throws SecurityException if a security manager is installed and the 479 * caller does not have permission to set the default prefix 480 */ setDefaultPrefix(String namespace, String prefix)481 public static void setDefaultPrefix(String namespace, String prefix) 482 throws XMLSecurityException { 483 JavaUtils.checkRegisterPermission(); 484 setNamespacePrefix(namespace, prefix); 485 } 486 setNamespacePrefix(String namespace, String prefix)487 private static void setNamespacePrefix(String namespace, String prefix) 488 throws XMLSecurityException { 489 if (prefixMappings.containsValue(prefix)) { 490 String storedPrefix = prefixMappings.get(namespace); 491 if (!storedPrefix.equals(prefix)) { 492 Object exArgs[] = { prefix, namespace, storedPrefix }; 493 494 throw new XMLSecurityException("prefix.AlreadyAssigned", exArgs); 495 } 496 } 497 498 if (Constants.SignatureSpecNS.equals(namespace)) { 499 XMLUtils.setDsPrefix(prefix); 500 } else if (Constants.SignatureSpec11NS.equals(namespace)) { 501 XMLUtils.setDs11Prefix(prefix); 502 } else if (EncryptionConstants.EncryptionSpecNS.equals(namespace)) { 503 XMLUtils.setXencPrefix(prefix); 504 } 505 prefixMappings.put(namespace, prefix); 506 } 507 508 /** 509 * This method registers the default prefixes. 510 */ registerDefaultPrefixes()511 public static void registerDefaultPrefixes() throws XMLSecurityException { 512 setNamespacePrefix("http://www.w3.org/2000/09/xmldsig#", "ds"); 513 setNamespacePrefix("http://www.w3.org/2001/04/xmlenc#", "xenc"); 514 setNamespacePrefix("http://www.w3.org/2009/xmlenc11#", "xenc11"); 515 setNamespacePrefix("http://www.xmlsecurity.org/experimental#", "experimental"); 516 setNamespacePrefix("http://www.w3.org/2002/04/xmldsig-filter2", "dsig-xpath-old"); 517 setNamespacePrefix("http://www.w3.org/2002/06/xmldsig-filter2", "dsig-xpath"); 518 setNamespacePrefix("http://www.w3.org/2001/10/xml-exc-c14n#", "ec"); 519 setNamespacePrefix( 520 "http://www.nue.et-inf.uni-siegen.de/~geuer-pollmann/#xpathFilter", "xx" 521 ); 522 setNamespacePrefix("http://www.w3.org/2009/xmldsig11#", "dsig11"); 523 } 524 525 /** 526 * Method getDefaultPrefix 527 * 528 * @param namespace 529 * @return the default prefix bind to this element. 530 */ getDefaultPrefix(String namespace)531 public static String getDefaultPrefix(String namespace) { 532 return prefixMappings.get(namespace); 533 } 534 535 /** 536 * New value for the wrapped XML element that this object is a proxy for. 537 * 538 * @param elem New element 539 * 540 * @see #getElement() 541 */ setElement(Element elem)542 protected void setElement(Element elem) { 543 wrappedElement = elem; 544 } 545 546 /** 547 * Set a new value for the wrapped document that this object is a proxy for. 548 * 549 * @param doc New document object being wrapped. 550 * 551 * @see #getDocument() 552 */ setDocument(Document doc)553 protected void setDocument(Document doc) { 554 wrappedDoc = doc; 555 } 556 getLocalAttribute(String attrName)557 protected String getLocalAttribute(String attrName) { 558 return getElement().getAttributeNS(null, attrName); 559 } 560 setLocalAttribute(String attrName, String value)561 protected void setLocalAttribute(String attrName, String value) { 562 getElement().setAttributeNS(null, attrName, value); 563 } 564 setLocalIdAttribute(String attrName, String value)565 protected void setLocalIdAttribute(String attrName, String value) { 566 567 if (value != null) { 568 Attr attr = getDocument().createAttributeNS(null, attrName); 569 attr.setValue(value); 570 getElement().setAttributeNodeNS(attr); 571 getElement().setIdAttributeNode(attr, true); 572 } 573 else { 574 getElement().removeAttributeNS(null, attrName); 575 } 576 } 577 getFirstChild()578 protected Node getFirstChild() { 579 return getElement().getFirstChild(); 580 } 581 582 } 583