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.signature;
  24 
  25 import java.io.IOException;
  26 import java.util.ArrayList;
  27 import java.util.HashMap;
  28 import java.util.Iterator;
  29 import java.util.List;
  30 import java.util.Map;
  31 import java.util.Set;
  32 
  33 import javax.xml.parsers.ParserConfigurationException;
  34 
  35 import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException;
  36 import com.sun.org.apache.xml.internal.security.c14n.InvalidCanonicalizerException;
  37 import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException;
  38 import com.sun.org.apache.xml.internal.security.transforms.Transforms;
  39 import com.sun.org.apache.xml.internal.security.utils.Constants;
  40 import com.sun.org.apache.xml.internal.security.utils.I18n;
  41 import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy;
  42 import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
  43 import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver;
  44 import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverSpi;
  45 import org.w3c.dom.Attr;
  46 import org.w3c.dom.DOMException;
  47 import org.w3c.dom.Document;
  48 import org.w3c.dom.Element;
  49 import org.w3c.dom.Node;
  50 import org.xml.sax.SAXException;
  51 
  52 /**
  53  * Handles <code>&lt;ds:Manifest&gt;</code> elements.
  54  * <p> This element holds the <code>Reference</code> elements</p>
  55  */
  56 public class Manifest extends SignatureElementProxy {
  57     
  58     /**
  59      * The maximum number of references per Manifest, if secure validation is enabled.
  60      */
  61     public static final int MAXIMUM_REFERENCE_COUNT = 30;
  62 
  63     /** {@link org.apache.commons.logging} logging facility */
  64     private static java.util.logging.Logger log = 
  65         java.util.logging.Logger.getLogger(Manifest.class.getName());
  66 
  67     /** Field references */
  68     private List<Reference> references;
  69     private Element[] referencesEl;
  70 
  71     /** Field verificationResults[] */
  72     private boolean verificationResults[] = null;
  73 
  74     /** Field resolverProperties */
  75     private Map<String, String> resolverProperties = null;
  76 
  77     /** Field perManifestResolvers */
  78     private List<ResourceResolver> perManifestResolvers = null;
  79     
  80     private boolean secureValidation;
  81 
  82     /**
  83      * Constructs {@link Manifest}
  84      *
  85      * @param doc the {@link Document} in which <code>XMLsignature</code> is placed
  86      */
  87     public Manifest(Document doc) {
  88         super(doc);
  89 
  90         XMLUtils.addReturnToElement(this.constructionElement);
  91 
  92         this.references = new ArrayList<Reference>();
  93     }
  94 
  95     /**
  96      * Constructor Manifest
  97      *
  98      * @param element
  99      * @param baseURI
 100      * @throws XMLSecurityException
 101      */
 102     public Manifest(Element element, String baseURI) throws XMLSecurityException {
 103         this(element, baseURI, false);
 104         
 105     }
 106     /**
 107      * Constructor Manifest
 108      *
 109      * @param element
 110      * @param baseURI
 111      * @param secureValidation
 112      * @throws XMLSecurityException
 113      */
 114     public Manifest(
 115         Element element, String baseURI, boolean secureValidation
 116     ) throws XMLSecurityException {
 117         super(element, baseURI);
 118         
 119         Attr attr = element.getAttributeNodeNS(null, "Id");
 120         if (attr != null) {
 121             element.setIdAttributeNode(attr, true);
 122         }
 123         this.secureValidation = secureValidation;
 124 
 125         // check out Reference children
 126         this.referencesEl = 
 127             XMLUtils.selectDsNodes(
 128                 this.constructionElement.getFirstChild(), Constants._TAG_REFERENCE
 129             );
 130         int le = this.referencesEl.length;
 131         if (le == 0) {
 132             // At least one Reference must be present. Bad.
 133             Object exArgs[] = { Constants._TAG_REFERENCE, Constants._TAG_MANIFEST };
 134 
 135             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
 136                                    I18n.translate("xml.WrongContent", exArgs));
 137         }
 138         
 139         if (secureValidation && le > MAXIMUM_REFERENCE_COUNT) {
 140             Object exArgs[] = { le, MAXIMUM_REFERENCE_COUNT };
 141             
 142             throw new XMLSecurityException("signature.tooManyReferences", exArgs);
 143         }
 144 
 145         // create List
 146         this.references = new ArrayList<Reference>(le);
 147 
 148         for (int i = 0; i < le; i++) {
 149             Element refElem = referencesEl[i];
 150             Attr refAttr = refElem.getAttributeNodeNS(null, "Id");
 151             if (refAttr != null) {
 152                 refElem.setIdAttributeNode(refAttr, true);
 153             }
 154             this.references.add(null);
 155         }
 156     }
 157 
 158     /**
 159      * This <code>addDocument</code> method is used to add a new resource to the
 160      * signed info. A {@link com.sun.org.apache.xml.internal.security.signature.Reference} is built
 161      * from the supplied values.
 162      *
 163      * @param baseURI the URI of the resource where the XML instance was stored
 164      * @param referenceURI <code>URI</code> attribute in <code>Reference</code> for specifying 
 165      * where data is
 166      * @param transforms com.sun.org.apache.xml.internal.security.signature.Transforms object with an ordered 
 167      * list of transformations to be performed.
 168      * @param digestURI The digest algorithm URI to be used.
 169      * @param referenceId
 170      * @param referenceType
 171      * @throws XMLSignatureException
 172      */
 173     public void addDocument(
 174         String baseURI, String referenceURI, Transforms transforms, 
 175         String digestURI, String referenceId, String referenceType
 176     ) throws XMLSignatureException {
 177         // the this.doc is handed implicitly by the this.getOwnerDocument()
 178         Reference ref = 
 179             new Reference(this.doc, baseURI, referenceURI, this, transforms, digestURI);
 180 
 181         if (referenceId != null) {
 182             ref.setId(referenceId);
 183         }
 184 
 185         if (referenceType != null) {
 186             ref.setType(referenceType);
 187         }
 188 
 189         // add Reference object to our cache vector
 190         this.references.add(ref);
 191 
 192         // add the Element of the Reference object to the Manifest/SignedInfo
 193         this.constructionElement.appendChild(ref.getElement());
 194         XMLUtils.addReturnToElement(this.constructionElement);
 195     }
 196 
 197     /**
 198      * The calculation of the DigestValues in the References must be after the
 199      * References are already added to the document and during the signing
 200      * process. This ensures that all necessary data is in place.
 201      *
 202      * @throws ReferenceNotInitializedException
 203      * @throws XMLSignatureException
 204      */
 205     public void generateDigestValues()
 206         throws XMLSignatureException, ReferenceNotInitializedException {
 207         for (int i = 0; i < this.getLength(); i++) {
 208             // update the cached Reference object, the Element content is automatically updated
 209             Reference currentRef = this.references.get(i);
 210             currentRef.generateDigestValue();
 211         }
 212     }
 213 
 214     /**
 215      * Return the nonnegative number of added references.
 216      *
 217      * @return the number of references
 218      */
 219     public int getLength() {
 220         return this.references.size();
 221     }
 222 
 223     /**
 224      * Return the <it>i</it><sup>th</sup> reference. Valid <code>i</code>
 225      * values are 0 to <code>{link@ getSize}-1</code>.
 226      *
 227      * @param i Index of the requested {@link Reference}
 228      * @return the <it>i</it><sup>th</sup> reference
 229      * @throws XMLSecurityException
 230      */
 231     public Reference item(int i) throws XMLSecurityException {
 232         if (this.references.get(i) == null) {
 233             // not yet constructed, so _we_ have to            
 234             Reference ref = 
 235                 new Reference(referencesEl[i], this.baseURI, this, secureValidation);
 236 
 237             this.references.set(i, ref);
 238         }
 239 
 240         return this.references.get(i);
 241     }
 242 
 243     /**
 244      * Sets the <code>Id</code> attribute
 245      *
 246      * @param Id the <code>Id</code> attribute in <code>ds:Manifest</code>
 247      */
 248     public void setId(String Id) {
 249         if (Id != null) {
 250             this.constructionElement.setAttributeNS(null, Constants._ATT_ID, Id);
 251             this.constructionElement.setIdAttributeNS(null, Constants._ATT_ID, true);
 252         }
 253     }
 254 
 255     /**
 256      * Returns the <code>Id</code> attribute
 257      *
 258      * @return the <code>Id</code> attribute in <code>ds:Manifest</code>
 259      */
 260     public String getId() {
 261         return this.constructionElement.getAttributeNS(null, Constants._ATT_ID);
 262     }
 263 
 264     /**
 265      * Used to do a <A HREF="http://www.w3.org/TR/xmldsig-core/#def-ValidationReference">reference
 266      * validation</A> of all enclosed references using the {@link Reference#verify} method.
 267      *
 268      * <p>This step loops through all {@link Reference}s and does verify the hash
 269      * values. If one or more verifications fail, the method returns
 270      * <code>false</code>. If <i>all</i> verifications are successful,
 271      * it returns <code>true</code>. The results of the individual reference
 272      * validations are available by using the {@link #getVerificationResult(int)} method
 273      *
 274      * @return true if all References verify, false if one or more do not verify.
 275      * @throws MissingResourceFailureException if a {@link Reference} does not verify 
 276      * (throws a {@link com.sun.org.apache.xml.internal.security.signature.ReferenceNotInitializedException} 
 277      * because of an uninitialized {@link XMLSignatureInput}
 278      * @see com.sun.org.apache.xml.internal.security.signature.Reference#verify
 279      * @see com.sun.org.apache.xml.internal.security.signature.SignedInfo#verify()
 280      * @see com.sun.org.apache.xml.internal.security.signature.MissingResourceFailureException
 281      * @throws XMLSecurityException
 282      */
 283     public boolean verifyReferences()
 284         throws MissingResourceFailureException, XMLSecurityException {
 285         return this.verifyReferences(false);
 286     }
 287 
 288     /**
 289      * Used to do a <A HREF="http://www.w3.org/TR/xmldsig-core/#def-ValidationReference">reference
 290      * validation</A> of all enclosed references using the {@link Reference#verify} method.
 291      *
 292      * <p>This step loops through all {@link Reference}s and does verify the hash
 293      * values. If one or more verifications fail, the method returns
 294      * <code>false</code>. If <i>all</i> verifications are successful,
 295      * it returns <code>true</code>. The results of the individual reference
 296      * validations are available by using the {@link #getVerificationResult(int)} method
 297      *
 298      * @param followManifests
 299      * @return true if all References verify, false if one or more do not verify.
 300      * @throws MissingResourceFailureException if a {@link Reference} does not verify 
 301      * (throws a {@link com.sun.org.apache.xml.internal.security.signature.ReferenceNotInitializedException} 
 302      * because of an uninitialized {@link XMLSignatureInput}
 303      * @see com.sun.org.apache.xml.internal.security.signature.Reference#verify
 304      * @see com.sun.org.apache.xml.internal.security.signature.SignedInfo#verify(boolean)
 305      * @see com.sun.org.apache.xml.internal.security.signature.MissingResourceFailureException
 306      * @throws XMLSecurityException
 307      */
 308     public boolean verifyReferences(boolean followManifests)
 309         throws MissingResourceFailureException, XMLSecurityException {
 310         if (referencesEl == null) {
 311             this.referencesEl =  
 312                 XMLUtils.selectDsNodes(
 313                     this.constructionElement.getFirstChild(), Constants._TAG_REFERENCE
 314                 );
 315         }
 316         if (log.isLoggable(java.util.logging.Level.FINE)) {
 317             log.log(java.util.logging.Level.FINE, "verify " + referencesEl.length + " References");
 318             log.log(java.util.logging.Level.FINE, "I am " + (followManifests
 319                 ? "" : "not") + " requested to follow nested Manifests");
 320         }
 321         if (referencesEl.length == 0) {
 322             throw new XMLSecurityException("empty");
 323         }
 324         if (secureValidation && referencesEl.length > MAXIMUM_REFERENCE_COUNT) {
 325             Object exArgs[] = { referencesEl.length, MAXIMUM_REFERENCE_COUNT };
 326                         
 327             throw new XMLSecurityException("signature.tooManyReferences", exArgs);
 328         }
 329 
 330         this.verificationResults = new boolean[referencesEl.length];
 331         boolean verify = true;
 332         for (int i = 0; i < this.referencesEl.length; i++) {
 333             Reference currentRef =
 334                 new Reference(referencesEl[i], this.baseURI, this, secureValidation);
 335 
 336             this.references.set(i, currentRef);
 337 
 338             // if only one item does not verify, the whole verification fails
 339             try {
 340                 boolean currentRefVerified = currentRef.verify();
 341 
 342                 this.setVerificationResult(i, currentRefVerified);
 343 
 344                 if (!currentRefVerified) {
 345                     verify = false;
 346                 }
 347                 if (log.isLoggable(java.util.logging.Level.FINE)) {
 348                     log.log(java.util.logging.Level.FINE, "The Reference has Type " + currentRef.getType());
 349                 }
 350 
 351                 // was verification successful till now and do we want to verify the Manifest?
 352                 if (verify && followManifests && currentRef.typeIsReferenceToManifest()) {
 353                     if (log.isLoggable(java.util.logging.Level.FINE)) {
 354                         log.log(java.util.logging.Level.FINE, "We have to follow a nested Manifest");
 355                     }
 356 
 357                     try {
 358                         XMLSignatureInput signedManifestNodes =
 359                             currentRef.dereferenceURIandPerformTransforms(null);
 360                         Set<Node> nl = signedManifestNodes.getNodeSet();
 361                         Manifest referencedManifest = null;
 362                         Iterator<Node> nlIterator = nl.iterator();
 363 
 364                         findManifest: while (nlIterator.hasNext()) {
 365                             Node n = nlIterator.next();
 366 
 367                             if ((n.getNodeType() == Node.ELEMENT_NODE) 
 368                                 && ((Element) n).getNamespaceURI().equals(Constants.SignatureSpecNS) 
 369                                 && ((Element) n).getLocalName().equals(Constants._TAG_MANIFEST)
 370                             ) {
 371                                 try {
 372                                     referencedManifest =
 373                                         new Manifest(
 374                                              (Element)n, signedManifestNodes.getSourceURI(), secureValidation
 375                                         );
 376                                     break findManifest;
 377                                 } catch (XMLSecurityException ex) {
 378                                     if (log.isLoggable(java.util.logging.Level.FINE)) {
 379                                         log.log(java.util.logging.Level.FINE, ex.getMessage(), ex);
 380                                     }
 381                                     // Hm, seems not to be a ds:Manifest
 382                                 }
 383                             }
 384                         }
 385 
 386                         if (referencedManifest == null) {
 387                             // The Reference stated that it points to a ds:Manifest
 388                             // but we did not find a ds:Manifest in the signed area
 389                             throw new MissingResourceFailureException("empty", currentRef);
 390                         }
 391 
 392                         referencedManifest.perManifestResolvers = this.perManifestResolvers;
 393                         referencedManifest.resolverProperties = this.resolverProperties;
 394 
 395                         boolean referencedManifestValid =
 396                             referencedManifest.verifyReferences(followManifests);
 397 
 398                         if (!referencedManifestValid) {
 399                             verify = false;
 400 
 401                             log.log(java.util.logging.Level.WARNING, "The nested Manifest was invalid (bad)");
 402                         } else {
 403                             if (log.isLoggable(java.util.logging.Level.FINE)) {
 404                                 log.log(java.util.logging.Level.FINE, "The nested Manifest was valid (good)");
 405                             }
 406                         }
 407                     } catch (IOException ex) {
 408                         throw new ReferenceNotInitializedException("empty", ex);
 409                     } catch (ParserConfigurationException ex) {
 410                         throw new ReferenceNotInitializedException("empty", ex);
 411                     } catch (SAXException ex) {
 412                         throw new ReferenceNotInitializedException("empty", ex);
 413                     }
 414                 }
 415             } catch (ReferenceNotInitializedException ex) {
 416                 Object exArgs[] = { currentRef.getURI() };
 417 
 418                 throw new MissingResourceFailureException(
 419                     "signature.Verification.Reference.NoInput", exArgs, ex, currentRef
 420                 );
 421             }
 422         }
 423 
 424         return verify;
 425     }
 426 
 427     /**
 428      * Method setVerificationResult
 429      *
 430      * @param index
 431      * @param verify
 432      */
 433     private void setVerificationResult(int index, boolean verify) {
 434         if (this.verificationResults == null) {
 435             this.verificationResults = new boolean[this.getLength()];
 436         }
 437 
 438         this.verificationResults[index] = verify;
 439     }
 440 
 441     /**
 442      * After verifying a {@link Manifest} or a {@link SignedInfo} using the
 443      * {@link Manifest#verifyReferences()} or {@link SignedInfo#verify()} methods,
 444      * the individual results can be retrieved with this method.
 445      *
 446      * @param index an index of into a {@link Manifest} or a {@link SignedInfo}
 447      * @return the results of reference validation at the specified index
 448      * @throws XMLSecurityException
 449      */
 450     public boolean getVerificationResult(int index) throws XMLSecurityException {
 451         if ((index < 0) || (index > this.getLength() - 1)) {
 452             Object exArgs[] = { Integer.toString(index), Integer.toString(this.getLength()) };
 453             Exception e =
 454                 new IndexOutOfBoundsException(
 455                     I18n.translate("signature.Verification.IndexOutOfBounds", exArgs)
 456                 );
 457 
 458             throw new XMLSecurityException("generic.EmptyMessage", e);
 459         }
 460 
 461         if (this.verificationResults == null) {
 462             try {
 463                 this.verifyReferences();
 464             } catch (Exception ex) {
 465                 throw new XMLSecurityException("generic.EmptyMessage", ex);
 466             }
 467         }
 468 
 469         return this.verificationResults[index];
 470     }
 471 
 472     /**
 473      * Adds Resource Resolver for retrieving resources at specified <code>URI</code> attribute 
 474      * in <code>reference</code> element
 475      *
 476      * @param resolver {@link ResourceResolver} can provide the implemenatin subclass of 
 477      * {@link ResourceResolverSpi} for retrieving resource.
 478      */
 479     public void addResourceResolver(ResourceResolver resolver) {
 480         if (resolver == null) {
 481             return;
 482         }
 483         if (perManifestResolvers == null) {
 484             perManifestResolvers = new ArrayList<ResourceResolver>();
 485         }
 486         this.perManifestResolvers.add(resolver);
 487     }
 488 
 489     /**
 490      * Adds Resource Resolver for retrieving resources at specified <code>URI</code> attribute 
 491      * in <code>reference</code> element
 492      *
 493      * @param resolverSpi the implementation subclass of {@link ResourceResolverSpi} for 
 494      * retrieving the resource.
 495      */
 496     public void addResourceResolver(ResourceResolverSpi resolverSpi) {
 497         if (resolverSpi == null) {
 498             return;
 499         }
 500         if (perManifestResolvers == null) {
 501             perManifestResolvers = new ArrayList<ResourceResolver>();
 502         }
 503         perManifestResolvers.add(new ResourceResolver(resolverSpi));
 504     }
 505 
 506     /**
 507      * Get the Per-Manifest Resolver List
 508      * @return the per-manifest Resolver List
 509      */
 510     public List<ResourceResolver> getPerManifestResolvers() {
 511         return perManifestResolvers;
 512     }
 513     
 514     /**
 515      * Get the resolver property map
 516      * @return the resolver property map
 517      */
 518     public Map<String, String> getResolverProperties() {
 519         return resolverProperties;
 520     }
 521     
 522     /**
 523      * Used to pass parameters like proxy servers etc to the ResourceResolver
 524      * implementation.
 525      *
 526      * @param key the key
 527      * @param value the value
 528      */
 529     public void setResolverProperty(String key, String value) {
 530         if (resolverProperties == null) {
 531             resolverProperties = new HashMap<String, String>(10);
 532         }
 533         this.resolverProperties.put(key, value);
 534     }
 535 
 536     /**
 537      * Returns the value at specified key
 538      *
 539      * @param key the key
 540      * @return the value
 541      */
 542     public String getResolverProperty(String key) {
 543         return this.resolverProperties.get(key);
 544     }
 545 
 546     /**
 547      * Method getSignedContentItem
 548      *
 549      * @param i
 550      * @return The signed content of the i reference.
 551      *
 552      * @throws XMLSignatureException
 553      */
 554     public byte[] getSignedContentItem(int i) throws XMLSignatureException {
 555         try {
 556             return this.getReferencedContentAfterTransformsItem(i).getBytes();
 557         } catch (IOException ex) {
 558             throw new XMLSignatureException("empty", ex);
 559         } catch (CanonicalizationException ex) {
 560             throw new XMLSignatureException("empty", ex);
 561         } catch (InvalidCanonicalizerException ex) {
 562             throw new XMLSignatureException("empty", ex);
 563         } catch (XMLSecurityException ex) {
 564             throw new XMLSignatureException("empty", ex);
 565         }
 566     }
 567 
 568     /**
 569      * Method getReferencedContentPriorTransformsItem
 570      *
 571      * @param i
 572      * @return The contents before transformation of the reference i.
 573      * @throws XMLSecurityException
 574      */
 575     public XMLSignatureInput getReferencedContentBeforeTransformsItem(int i)
 576         throws XMLSecurityException {
 577         return this.item(i).getContentsBeforeTransformation();
 578     }
 579 
 580     /**
 581      * Method getReferencedContentAfterTransformsItem
 582      *
 583      * @param i
 584      * @return The contents after transformation of the reference i.
 585      * @throws XMLSecurityException
 586      */
 587     public XMLSignatureInput getReferencedContentAfterTransformsItem(int i)
 588         throws XMLSecurityException {
 589         return this.item(i).getContentsAfterTransformation();
 590     }
 591 
 592     /**
 593      * Method getSignedContentLength
 594      *
 595      * @return The number of references contained in this reference.
 596      */
 597     public int getSignedContentLength() {
 598         return this.getLength();
 599     }
 600 
 601     /**
 602      * Method getBaseLocalName
 603      *
 604      * @inheritDoc
 605      */
 606     public String getBaseLocalName() {
 607         return Constants._TAG_MANIFEST;
 608     }
 609 }