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