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