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.c14n.implementations;
24 
25 import java.io.IOException;
26 import java.io.OutputStream;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.SortedSet;
30 import java.util.TreeSet;
31 
32 import javax.xml.parsers.ParserConfigurationException;
33 
34 import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException;
35 import com.sun.org.apache.xml.internal.security.c14n.helper.C14nHelper;
36 import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput;
37 import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
38 import org.w3c.dom.Attr;
39 import org.w3c.dom.DOMException;
40 import org.w3c.dom.Document;
41 import org.w3c.dom.Element;
42 import org.w3c.dom.NamedNodeMap;
43 import org.w3c.dom.Node;
44 import org.xml.sax.SAXException;
45 
46 /**
47  * Implements <A HREF="http://www.w3.org/TR/2001/REC-xml-c14n-20010315">Canonical
48  * XML Version 1.0</A>, a W3C Recommendation from 15 March 2001.
49  *
50  */
51 public abstract class Canonicalizer20010315 extends CanonicalizerBase {
52 
53     private boolean firstCall = true;
54 
55     private final XmlAttrStack xmlattrStack;
56     private final boolean c14n11;
57 
58     /**
59      * Constructor Canonicalizer20010315
60      *
61      * @param includeComments
62      */
Canonicalizer20010315(boolean includeComments)63     public Canonicalizer20010315(boolean includeComments) {
64         this(includeComments, false);
65     }
66 
67     /**
68      * Constructor Canonicalizer20010315
69      *
70      * @param includeComments
71      * @param c14n11 Whether this is a Canonical XML 1.1 implementation or not
72      */
Canonicalizer20010315(boolean includeComments, boolean c14n11)73     public Canonicalizer20010315(boolean includeComments, boolean c14n11) {
74         super(includeComments);
75         xmlattrStack = new XmlAttrStack(c14n11);
76         this.c14n11 = c14n11;
77     }
78 
79 
80     /**
81      * Always throws a CanonicalizationException because this is inclusive c14n.
82      *
83      * @param xpathNodeSet
84      * @param inclusiveNamespaces
85      * @return none it always fails
86      * @throws CanonicalizationException always
87      */
engineCanonicalizeXPathNodeSet(Set<Node> xpathNodeSet, String inclusiveNamespaces)88     public byte[] engineCanonicalizeXPathNodeSet(Set<Node> xpathNodeSet, String inclusiveNamespaces)
89         throws CanonicalizationException {
90 
91         /** $todo$ well, should we throw UnsupportedOperationException ? */
92         throw new CanonicalizationException("c14n.Canonicalizer.UnsupportedOperation");
93     }
94 
95     /**
96      * Always throws a CanonicalizationException because this is inclusive c14n.
97      *
98      * @param rootNode
99      * @param inclusiveNamespaces
100      * @return none it always fails
101      * @throws CanonicalizationException
102      */
engineCanonicalizeSubTree(Node rootNode, String inclusiveNamespaces)103     public byte[] engineCanonicalizeSubTree(Node rootNode, String inclusiveNamespaces)
104         throws CanonicalizationException {
105 
106         /** $todo$ well, should we throw UnsupportedOperationException ? */
107         throw new CanonicalizationException("c14n.Canonicalizer.UnsupportedOperation");
108     }
109 
110     /**
111      * Always throws a CanonicalizationException because this is inclusive c14n.
112      *
113      * @param rootNode
114      * @param inclusiveNamespaces
115      * @return none it always fails
116      * @throws CanonicalizationException
117      */
engineCanonicalizeSubTree( Node rootNode, String inclusiveNamespaces, boolean propagateDefaultNamespace)118     public byte[] engineCanonicalizeSubTree(
119             Node rootNode, String inclusiveNamespaces, boolean propagateDefaultNamespace)
120             throws CanonicalizationException {
121 
122         /** $todo$ well, should we throw UnsupportedOperationException ? */
123         throw new CanonicalizationException("c14n.Canonicalizer.UnsupportedOperation");
124     }
125 
126     /**
127      * Output the Attr[]s for the given element.
128      * <br>
129      * The code of this method is a copy of {@link #outputAttributes(Element,
130      * NameSpaceSymbTable, Map<String, byte[]>)},
131      * whereas it takes into account that subtree-c14n is -- well -- subtree-based.
132      * So if the element in question isRoot of c14n, it's parent is not in the
133      * node set, as well as all other ancestors.
134      *
135      * @param element
136      * @param ns
137      * @param cache
138      * @throws CanonicalizationException, DOMException, IOException
139      */
140     @Override
outputAttributesSubtree(Element element, NameSpaceSymbTable ns, Map<String, byte[]> cache)141     protected void outputAttributesSubtree(Element element, NameSpaceSymbTable ns,
142                                            Map<String, byte[]> cache)
143         throws CanonicalizationException, DOMException, IOException {
144         if (!element.hasAttributes() && !firstCall) {
145             return;
146         }
147         // result will contain the attrs which have to be output
148         SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
149 
150         if (element.hasAttributes()) {
151             NamedNodeMap attrs = element.getAttributes();
152             int attrsLength = attrs.getLength();
153 
154             for (int i = 0; i < attrsLength; i++) {
155                 Attr attribute = (Attr) attrs.item(i);
156                 String NUri = attribute.getNamespaceURI();
157                 String NName = attribute.getLocalName();
158                 String NValue = attribute.getValue();
159 
160                 if (!XMLNS_URI.equals(NUri)) {
161                     //It's not a namespace attr node. Add to the result and continue.
162                     result.add(attribute);
163                 } else if (!(XML.equals(NName) && XML_LANG_URI.equals(NValue))) {
164                     //The default mapping for xml must not be output.
165                     Node n = ns.addMappingAndRender(NName, NValue, attribute);
166 
167                     if (n != null) {
168                         //Render the ns definition
169                         result.add((Attr)n);
170                         if (C14nHelper.namespaceIsRelative(attribute)) {
171                             Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
172                             throw new CanonicalizationException(
173                                 "c14n.Canonicalizer.RelativeNamespace", exArgs
174                             );
175                         }
176                     }
177                 }
178             }
179         }
180 
181         if (firstCall) {
182             //It is the first node of the subtree
183             //Obtain all the namespaces defined in the parents, and added to the output.
184             ns.getUnrenderedNodes(result);
185             //output the attributes in the xml namespace.
186             xmlattrStack.getXmlnsAttr(result);
187             firstCall = false;
188         }
189 
190         OutputStream writer = getWriter();
191         //we output all Attrs which are available
192         for (Attr attr : result) {
193             outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache);
194         }
195     }
196 
197     /**
198      * Output the Attr[]s for the given element.
199      * <br>
200      * IMPORTANT: This method expects to work on a modified DOM tree, i.e. a DOM which has
201      * been prepared using {@link com.sun.org.apache.xml.internal.security.utils.XMLUtils#circumventBug2650(
202      * org.w3c.dom.Document)}.
203      *
204      * @param element
205      * @param ns
206      * @param cache
207      * @throws CanonicalizationException, DOMException, IOException
208      */
209     @Override
outputAttributes(Element element, NameSpaceSymbTable ns, Map<String, byte[]> cache)210     protected void outputAttributes(Element element, NameSpaceSymbTable ns,
211                                     Map<String, byte[]> cache)
212         throws CanonicalizationException, DOMException, IOException {
213         // result will contain the attrs which have to be output
214         xmlattrStack.push(ns.getLevel());
215         boolean isRealVisible = isVisibleDO(element, ns.getLevel()) == 1;
216         SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
217 
218         if (element.hasAttributes()) {
219             NamedNodeMap attrs = element.getAttributes();
220             int attrsLength = attrs.getLength();
221 
222             for (int i = 0; i < attrsLength; i++) {
223                 Attr attribute = (Attr) attrs.item(i);
224                 String NUri = attribute.getNamespaceURI();
225                 String NName = attribute.getLocalName();
226                 String NValue = attribute.getValue();
227 
228                 if (!XMLNS_URI.equals(NUri)) {
229                     //A non namespace definition node.
230                     if (XML_LANG_URI.equals(NUri)) {
231                         if (c14n11 && "id".equals(NName)) {
232                             if (isRealVisible) {
233                                 // treat xml:id like any other attribute
234                                 // (emit it, but don't inherit it)
235                                 result.add(attribute);
236                             }
237                         } else {
238                             xmlattrStack.addXmlnsAttr(attribute);
239                         }
240                     } else if (isRealVisible) {
241                         //The node is visible add the attribute to the list of output attributes.
242                         result.add(attribute);
243                     }
244                 } else if (!XML.equals(NName) || !XML_LANG_URI.equals(NValue)) {
245                     /* except omit namespace node with local name xml, which defines
246                      * the xml prefix, if its string value is http://www.w3.org/XML/1998/namespace.
247                      */
248                     //add the prefix binding to the ns symb table.
249                     if (isVisible(attribute))  {
250                         if (isRealVisible || !ns.removeMappingIfRender(NName)) {
251                             //The xpath select this node output it if needed.
252                             Node n = ns.addMappingAndRender(NName, NValue, attribute);
253                             if (n != null) {
254                                 result.add((Attr)n);
255                                 if (C14nHelper.namespaceIsRelative(attribute)) {
256                                     Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
257                                     throw new CanonicalizationException(
258                                         "c14n.Canonicalizer.RelativeNamespace", exArgs
259                                     );
260                                 }
261                             }
262                         }
263                     } else {
264                         if (isRealVisible && !XMLNS.equals(NName)) {
265                             ns.removeMapping(NName);
266                         } else {
267                             ns.addMapping(NName, NValue, attribute);
268                         }
269                     }
270                 }
271             }
272         }
273         if (isRealVisible) {
274             //The element is visible, handle the xmlns definition
275             Attr xmlns = element.getAttributeNodeNS(XMLNS_URI, XMLNS);
276             Node n = null;
277             if (xmlns == null) {
278                 //No xmlns def just get the already defined.
279                 n = ns.getMapping(XMLNS);
280             } else if (!isVisible(xmlns)) {
281                 //There is a definition but the xmlns is not selected by the xpath.
282                 //then xmlns=""
283                 n = ns.addMappingAndRender(
284                         XMLNS, "", getNullNode(xmlns.getOwnerDocument()));
285             }
286             //output the xmlns def if needed.
287             if (n != null) {
288                 result.add((Attr)n);
289             }
290             //Float all xml:* attributes of the unselected parent elements to this one.
291             xmlattrStack.getXmlnsAttr(result);
292             ns.getUnrenderedNodes(result);
293         }
294 
295         OutputStream writer = getWriter();
296         //we output all Attrs which are available
297         for (Attr attr : result) {
298             outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache);
299         }
300     }
301 
circumventBugIfNeeded(XMLSignatureInput input)302     protected void circumventBugIfNeeded(XMLSignatureInput input)
303         throws CanonicalizationException, ParserConfigurationException, IOException, SAXException {
304         if (!input.isNeedsToBeExpanded()) {
305             return;
306         }
307         Document doc = null;
308         if (input.getSubNode() != null) {
309             doc = XMLUtils.getOwnerDocument(input.getSubNode());
310         } else {
311             doc = XMLUtils.getOwnerDocument(input.getNodeSet());
312         }
313         XMLUtils.circumventBug2650(doc);
314     }
315 
316     @Override
handleParent(Element e, NameSpaceSymbTable ns)317     protected void handleParent(Element e, NameSpaceSymbTable ns) {
318         if (!e.hasAttributes() && e.getNamespaceURI() == null) {
319             return;
320         }
321         xmlattrStack.push(-1);
322         NamedNodeMap attrs = e.getAttributes();
323         int attrsLength = attrs.getLength();
324         for (int i = 0; i < attrsLength; i++) {
325             Attr attribute = (Attr) attrs.item(i);
326             String NName = attribute.getLocalName();
327             String NValue = attribute.getNodeValue();
328 
329             if (XMLNS_URI.equals(attribute.getNamespaceURI())) {
330                 if (!XML.equals(NName) || !XML_LANG_URI.equals(NValue)) {
331                     ns.addMapping(NName, NValue, attribute);
332                 }
333             } else if (XML_LANG_URI.equals(attribute.getNamespaceURI())
334                 && (!c14n11 || !"id".equals(NName))) {
335                 xmlattrStack.addXmlnsAttr(attribute);
336             }
337         }
338         if (e.getNamespaceURI() != null) {
339             String NName = e.getPrefix();
340             String NValue = e.getNamespaceURI();
341             String Name;
342             if (NName == null || NName.equals("")) {
343                 NName = "xmlns";
344                 Name = "xmlns";
345             } else {
346                 Name = "xmlns:" + NName;
347             }
348             Attr n = e.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/", Name);
349             n.setValue(NValue);
350             ns.addMapping(NName, NValue, n);
351         }
352     }
353 }
354