1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 package org.apache.xml.security.c14n.implementations;
20 
21 import java.io.IOException;
22 import java.util.Iterator;
23 import java.util.Set;
24 import java.util.SortedSet;
25 import java.util.TreeSet;
26 import javax.xml.parsers.ParserConfigurationException;
27 
28 import org.apache.xml.security.c14n.CanonicalizationException;
29 import org.apache.xml.security.c14n.helper.C14nHelper;
30 import org.apache.xml.security.signature.XMLSignatureInput;
31 import org.apache.xml.security.transforms.params.InclusiveNamespaces;
32 import org.apache.xml.security.utils.Constants;
33 import org.apache.xml.security.utils.XMLUtils;
34 import org.w3c.dom.Attr;
35 import org.w3c.dom.Document;
36 import org.w3c.dom.Element;
37 import org.w3c.dom.NamedNodeMap;
38 import org.w3c.dom.Node;
39 import org.xml.sax.SAXException;
40 
41 /**
42  * Implements &quot; <A
43  * HREF="http://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/">Exclusive XML
44  * Canonicalization, Version 1.0 </A>&quot; <BR />
45  * Credits: During restructuring of the Canonicalizer framework, Ren??
46  * Kollmorgen from Software AG submitted an implementation of ExclC14n which
47  * fitted into the old architecture and which based heavily on my old (and slow)
48  * implementation of "Canonical XML". A big "thank you" to Ren?? for this.
49  * <BR />
50  * <i>THIS </i> implementation is a complete rewrite of the algorithm.
51  *
52  * @author Christian Geuer-Pollmann <geuerp@apache.org>
53  * @version $Revision: 1147448 $
54  * @see <a href="http://www.w3.org/TR/2002/REC-xml-exc-c14n-20020718/ Exclusive#">
55  *          XML Canonicalization, Version 1.0</a>
56  */
57 public abstract class Canonicalizer20010315Excl extends CanonicalizerBase {
58 
59     private static final String XML_LANG_URI = Constants.XML_LANG_SPACE_SpecNS;
60     private static final String XMLNS_URI = Constants.NamespaceSpecNS;
61 
62     /**
63       * This Set contains the names (Strings like "xmlns" or "xmlns:foo") of
64       * the inclusive namespaces.
65       */
66     private SortedSet<String> inclusiveNSSet;
67 
68     private final SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
69 
70     /**
71      * Constructor Canonicalizer20010315Excl
72      *
73      * @param includeComments
74      */
Canonicalizer20010315Excl(boolean includeComments)75     public Canonicalizer20010315Excl(boolean includeComments) {
76         super(includeComments);
77     }
78 
79     /**
80      * Method engineCanonicalizeSubTree
81      * @inheritDoc
82      * @param rootNode
83      *
84      * @throws CanonicalizationException
85      */
engineCanonicalizeSubTree(Node rootNode)86     public byte[] engineCanonicalizeSubTree(Node rootNode)
87         throws CanonicalizationException {
88         return engineCanonicalizeSubTree(rootNode, "", null);
89     }
90 
91     /**
92      * Method engineCanonicalizeSubTree
93      *  @inheritDoc
94      * @param rootNode
95      * @param inclusiveNamespaces
96      *
97      * @throws CanonicalizationException
98      */
engineCanonicalizeSubTree( Node rootNode, String inclusiveNamespaces )99     public byte[] engineCanonicalizeSubTree(
100         Node rootNode, String inclusiveNamespaces
101     ) throws CanonicalizationException {
102         return engineCanonicalizeSubTree(rootNode, inclusiveNamespaces, null);
103     }
104 
105     /**
106      * Method engineCanonicalizeSubTree
107      * @param rootNode
108      * @param inclusiveNamespaces
109      * @param excl A element to exclude from the c14n process.
110      * @return the rootNode c14n.
111      * @throws CanonicalizationException
112      */
engineCanonicalizeSubTree( Node rootNode, String inclusiveNamespaces, Node excl )113     public byte[] engineCanonicalizeSubTree(
114         Node rootNode, String inclusiveNamespaces, Node excl
115     ) throws CanonicalizationException{
116         inclusiveNSSet = InclusiveNamespaces.prefixStr2Set(inclusiveNamespaces);
117         return super.engineCanonicalizeSubTree(rootNode, excl);
118     }
119 
120     /**
121      *
122      * @param rootNode
123      * @param inclusiveNamespaces
124      * @return the rootNode c14n.
125      * @throws CanonicalizationException
126      */
engineCanonicalize( XMLSignatureInput rootNode, String inclusiveNamespaces )127     public byte[] engineCanonicalize(
128         XMLSignatureInput rootNode, String inclusiveNamespaces
129     ) throws CanonicalizationException {
130         inclusiveNSSet = InclusiveNamespaces.prefixStr2Set(inclusiveNamespaces);
131         return super.engineCanonicalize(rootNode);
132     }
133 
134     /**
135      * Method engineCanonicalizeXPathNodeSet
136      * @inheritDoc
137      * @param xpathNodeSet
138      * @param inclusiveNamespaces
139      * @throws CanonicalizationException
140      */
engineCanonicalizeXPathNodeSet( Set<Node> xpathNodeSet, String inclusiveNamespaces )141     public byte[] engineCanonicalizeXPathNodeSet(
142         Set<Node> xpathNodeSet, String inclusiveNamespaces
143     ) throws CanonicalizationException {
144         inclusiveNSSet = InclusiveNamespaces.prefixStr2Set(inclusiveNamespaces);
145         return super.engineCanonicalizeXPathNodeSet(xpathNodeSet);
146     }
147 
148     @Override
handleAttributesSubtree(Element element, NameSpaceSymbTable ns)149     protected Iterator<Attr> handleAttributesSubtree(Element element, NameSpaceSymbTable ns)
150         throws CanonicalizationException {
151         // result will contain the attrs which have to be output
152         final SortedSet<Attr> result = this.result;
153         result.clear();
154 
155         // The prefix visibly utilized (in the attribute or in the name) in
156         // the element
157         SortedSet<String> visiblyUtilized = new TreeSet<String>();
158         if (inclusiveNSSet != null && !inclusiveNSSet.isEmpty()) {
159             visiblyUtilized.addAll(inclusiveNSSet);
160         }
161 
162         if (element.hasAttributes()) {
163             NamedNodeMap attrs = element.getAttributes();
164             int attrsLength = attrs.getLength();
165             for (int i = 0; i < attrsLength; i++) {
166                 Attr attribute = (Attr) attrs.item(i);
167                 String NName = attribute.getLocalName();
168                 String NNodeValue = attribute.getNodeValue();
169 
170                 if (!XMLNS_URI.equals(attribute.getNamespaceURI())) {
171                     // Not a namespace definition.
172                     // The Element is output element, add the prefix (if used) to
173                     // visiblyUtilized
174                     String prefix = attribute.getPrefix();
175                     if (prefix != null && !(prefix.equals(XML) || prefix.equals(XMLNS))) {
176                         visiblyUtilized.add(prefix);
177                     }
178                     // Add to the result.
179                     result.add(attribute);
180                 } else if (!(XML.equals(NName) && XML_LANG_URI.equals(NNodeValue))
181                     && ns.addMapping(NName, NNodeValue, attribute)
182                     && C14nHelper.namespaceIsRelative(NNodeValue)) {
183                     // The default mapping for xml must not be output.
184                     // New definition check if it is relative.
185                     Object exArgs[] = {element.getTagName(), NName, attribute.getNodeValue()};
186                     throw new CanonicalizationException(
187                         "c14n.Canonicalizer.RelativeNamespace", exArgs
188                     );
189                 }
190             }
191         }
192         String prefix = null;
193         if (element.getNamespaceURI() != null
194             && !(element.getPrefix() == null || element.getPrefix().length() == 0)) {
195             prefix = element.getPrefix();
196         } else {
197             prefix = XMLNS;
198         }
199         visiblyUtilized.add(prefix);
200 
201         for (String s : visiblyUtilized) {
202             Attr key = ns.getMapping(s);
203             if (key != null) {
204                 result.add(key);
205             }
206         }
207 
208         return result.iterator();
209     }
210 
211     /**
212      * @inheritDoc
213      * @param element
214      * @throws CanonicalizationException
215      */
216     @Override
handleAttributes(Element element, NameSpaceSymbTable ns)217     protected final Iterator<Attr> handleAttributes(Element element, NameSpaceSymbTable ns)
218         throws CanonicalizationException {
219         // result will contain the attrs which have to be output
220         final SortedSet<Attr> result = this.result;
221         result.clear();
222 
223         // The prefix visibly utilized (in the attribute or in the name) in
224         // the element
225         Set<String> visiblyUtilized = null;
226         // It's the output selected.
227         boolean isOutputElement = isVisibleDO(element, ns.getLevel()) == 1;
228         if (isOutputElement) {
229             visiblyUtilized = new TreeSet<String>();
230             if (inclusiveNSSet != null && !inclusiveNSSet.isEmpty()) {
231                 visiblyUtilized.addAll(inclusiveNSSet);
232             }
233         }
234 
235         if (element.hasAttributes()) {
236             NamedNodeMap attrs = element.getAttributes();
237             int attrsLength = attrs.getLength();
238             for (int i = 0; i < attrsLength; i++) {
239                 Attr attribute = (Attr) attrs.item(i);
240 
241                 String NName = attribute.getLocalName();
242                 String NNodeValue = attribute.getNodeValue();
243 
244                 if (!XMLNS_URI.equals(attribute.getNamespaceURI())) {
245                     if (isVisible(attribute) && isOutputElement) {
246                         // The Element is output element, add the prefix (if used)
247                         // to visibyUtilized
248                         String prefix = attribute.getPrefix();
249                         if (prefix != null && !(prefix.equals(XML) || prefix.equals(XMLNS))) {
250                             visiblyUtilized.add(prefix);
251                         }
252                         // Add to the result.
253                         result.add(attribute);
254                     }
255                 } else if (isOutputElement && !isVisible(attribute) && !XMLNS.equals(NName)) {
256                     ns.removeMappingIfNotRender(NName);
257                 } else {
258                     if (!isOutputElement && isVisible(attribute)
259                         && inclusiveNSSet.contains(NName)
260                         && !ns.removeMappingIfRender(NName)) {
261                         Node n = ns.addMappingAndRender(NName, NNodeValue, attribute);
262                         if (n != null) {
263                             result.add((Attr)n);
264                             if (C14nHelper.namespaceIsRelative(attribute)) {
265                                 Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
266                                 throw new CanonicalizationException(
267                                     "c14n.Canonicalizer.RelativeNamespace", exArgs
268                                 );
269                             }
270                         }
271                     }
272 
273                     if (ns.addMapping(NName, NNodeValue, attribute)
274                         && C14nHelper.namespaceIsRelative(NNodeValue)) {
275                         // New definition check if it is relative
276                         Object exArgs[] = { element.getTagName(), NName, attribute.getNodeValue() };
277                         throw new CanonicalizationException(
278                             "c14n.Canonicalizer.RelativeNamespace", exArgs
279                         );
280                     }
281                 }
282             }
283         }
284 
285         if (isOutputElement) {
286             // The element is visible, handle the xmlns definition
287             Attr xmlns = element.getAttributeNodeNS(XMLNS_URI, XMLNS);
288             if (xmlns != null && !isVisible(xmlns)) {
289                 // There is a definition but the xmlns is not selected by the
290                 // xpath. then xmlns=""
291                 ns.addMapping(XMLNS, "", nullNode);
292             }
293 
294             String prefix = null;
295             if (element.getNamespaceURI() != null
296                 && !(element.getPrefix() == null || element.getPrefix().length() == 0)) {
297                 prefix = element.getPrefix();
298             } else {
299                 prefix = XMLNS;
300             }
301             visiblyUtilized.add(prefix);
302 
303             for (String s : visiblyUtilized) {
304                 Attr key = ns.getMapping(s);
305                 if (key != null) {
306                     result.add(key);
307                 }
308             }
309         }
310 
311         return result.iterator();
312     }
313 
circumventBugIfNeeded(XMLSignatureInput input)314     protected void circumventBugIfNeeded(XMLSignatureInput input)
315         throws CanonicalizationException, ParserConfigurationException,
316                IOException, SAXException {
317         if (!input.isNeedsToBeExpanded() || inclusiveNSSet.isEmpty() || inclusiveNSSet.isEmpty()) {
318             return;
319         }
320         Document doc = null;
321         if (input.getSubNode() != null) {
322             doc = XMLUtils.getOwnerDocument(input.getSubNode());
323         } else {
324             doc = XMLUtils.getOwnerDocument(input.getNodeSet());
325         }
326         XMLUtils.circumventBug2650(doc);
327     }
328 }
329