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