net.ripe.rpki.validator.fetchers.ValidatingCertificateRepositoryObjectFetcher.java Source code

Java tutorial

Introduction

Here is the source code for net.ripe.rpki.validator.fetchers.ValidatingCertificateRepositoryObjectFetcher.java

Source

/**
 * The BSD License
 *
 * Copyright (c) 2010-2012 RIPE NCC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *   - Neither the name of the RIPE NCC nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package net.ripe.rpki.validator.fetchers;

import net.ripe.rpki.commons.crypto.CertificateRepositoryObject;
import net.ripe.rpki.commons.crypto.cms.manifest.ManifestCms;
import net.ripe.rpki.commons.crypto.crl.CrlLocator;
import net.ripe.rpki.commons.crypto.crl.X509Crl;
import net.ripe.rpki.commons.util.Specification;
import net.ripe.rpki.commons.validation.ValidationLocation;
import net.ripe.rpki.commons.validation.ValidationOptions;
import net.ripe.rpki.commons.validation.ValidationResult;
import net.ripe.rpki.commons.validation.ValidationString;
import net.ripe.rpki.commons.validation.objectvalidators.CertificateRepositoryObjectValidationContext;
import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;

import java.io.File;
import java.net.URI;

import static net.ripe.rpki.commons.validation.ValidationString.*;

public class ValidatingCertificateRepositoryObjectFetcher implements CertificateRepositoryObjectFetcher {

    private static final Logger LOG = Logger.getLogger(ValidatingCertificateRepositoryObjectFetcher.class);

    private final CertificateRepositoryObjectFetcher fetcher;
    private CertificateRepositoryObjectFetcher outerMostDecorator;

    private ValidationOptions options;

    /**
     * A validating CROFetcher. All objects retrieved are being validated. Invalid objects result in
     * null values being returned instead. Note that validation requires a CrlLocator. Because other
     * decorating CROFetchers are likely to be used (notifying, caching) a setter is provided to
     * allow for the outermost decorator to be used for the CRL retrieval.
     */
    public ValidatingCertificateRepositoryObjectFetcher(CertificateRepositoryObjectFetcher fetcher) {
        this(fetcher, new ValidationOptions());
    }

    /**
     * A validating CROFetcher. All objects retrieved are being validated. Invalid objects result in
     * null values being returned instead. Note that validation requires a CrlLocator. Because other
     * decorating CROFetchers are likely to be used (notifying, caching) a setter is provided to
     * allow for the outermost decorator to be used for the CRL retrieval.
     */
    public ValidatingCertificateRepositoryObjectFetcher(CertificateRepositoryObjectFetcher fetcher,
            ValidationOptions options) {
        this.fetcher = fetcher;
        this.options = options;
        this.outerMostDecorator = this;
    }

    /**
     * Set the outermost decorator which will be used as the CrlLocator for validation
     */
    public void setOuterMostDecorator(CertificateRepositoryObjectFetcher outerMostDecorator) {
        this.outerMostDecorator = outerMostDecorator;
    }

    @Override
    public X509Crl getCrl(URI uri, CertificateRepositoryObjectValidationContext context, ValidationResult result) {
        Validate.notNull(context);
        Validate.notNull(result);
        Validate.notNull(uri);

        /*
         * Now that we have the ConsistentObjectFetcher I believe we can simplify this code. We have
         * already checked the hash, a lookup by url here will hit the the store.
         *
         * So we don't need to do step 2 and 3. Just get it.
         *
         * Note: the manifest will actually use this code for its own validation. So in the unlikely
         * case that we have a manifest that mentions a crl that invalidates that manifest the
         * manifest is still rejected.
         *
         * I am not sure if we should care about this. This is a serious error on the publication
         * side, and recovering from it here is quite painful.
         */

        /*
         * Three step process:
         * - Get the CRL and validate it ignoring hash for content
         * - Get its manifest and validate the manifest based on this CRL
         * - Re-validate the CRL for its hash
         */

        // 1: Get the CRL without hash validation
        X509Crl crl = fetcher.getCrl(uri, context, result);
        if (crl == null) {
            return null;
        }
        crl = (X509Crl) processCertificateRepositoryObject(uri, context, result, crl);

        // 2: Get the manifest and validate it based on this CRL
        ManifestCms manifest = getManifestValidatedForCrl(uri, context, result, crl);
        result.rejectIfFalse(manifest != null, ValidationString.CRL_MANIFEST_VALID);
        if (manifest == null) {
            return null;
        }

        // 3: Re-validate the hash for this CRL
        checkHashValueForCrl(uri, result, crl, manifest);
        if (result.hasFailureForCurrentLocation()) {
            return null;
        }

        return crl;
    }

    @Override
    public ManifestCms getManifest(URI uri, CertificateRepositoryObjectValidationContext context,
            ValidationResult result) {
        Validate.notNull(context);
        Validate.notNull(result);

        try {
            ManifestCms manifestCms = fetcher.getManifest(uri, context, result);
            return (ManifestCms) processCertificateRepositoryObject(uri, context, result, manifestCms);
        } catch (Exception e) {
            LOG.error("There was an exception trying to get manifest: " + uri.toString(), e);
            result.rejectIfFalse(false, OBJECTS_GENERAL_PARSING, uri.toString());
            return null;
        }

    }

    @Override
    public CertificateRepositoryObject getObject(URI uri, CertificateRepositoryObjectValidationContext context,
            Specification<byte[]> fileContentSpecification, ValidationResult result) {
        Validate.notNull(context);
        Validate.notNull(result);

        try {
            CertificateRepositoryObject certificateRepositoryObject = fetcher.getObject(uri, context,
                    fileContentSpecification, result);
            return processCertificateRepositoryObject(uri, context, result, certificateRepositoryObject);
        } catch (Exception e) {
            LOG.error("There was an exception trying to get object for uri: " + uri.toString(), e);
            result.rejectIfFalse(false, OBJECTS_GENERAL_PARSING, uri.toString());
            return null;
        }
    }

    @Override
    public void prefetch(URI uri, ValidationResult result) {
        fetcher.prefetch(uri, result);
    }

    private CertificateRepositoryObject processCertificateRepositoryObject(URI uri,
            CertificateRepositoryObjectValidationContext context, ValidationResult result,
            CertificateRepositoryObject certificateRepositoryObject) {
        if (certificateRepositoryObject == null) {
            return null;
        }
        certificateRepositoryObject.validate(uri.toString(), context, outerMostDecorator, options, result);
        if (result.hasFailureForCurrentLocation()) {
            return null;
        }
        return certificateRepositoryObject;
    }

    private void checkHashValueForCrl(URI uri, ValidationResult result, X509Crl crl, ManifestCms manifest) {
        String crlFileName = new File(uri.getRawPath()).getName();

        // FIXME: is this really the right way to go with error locations?
        //        this way the manifest check error does end up with the CRL which I believe is right..
        result.setLocation(new ValidationLocation(uri));
        result.rejectIfFalse(manifest.containsFile(crlFileName),
                ValidationString.VALIDATOR_MANIFEST_DOES_NOT_CONTAIN_FILE, crlFileName);
        if (result.hasFailureForCurrentLocation()) {
            return;
        }
        result.rejectIfFalse(manifest.verifyFileContents(crlFileName, crl.getEncoded()),
                ValidationString.VALIDATOR_FILE_CONTENT);
    }

    private ManifestCms getManifestValidatedForCrl(final URI crlUri,
            CertificateRepositoryObjectValidationContext context, ValidationResult result, final X509Crl crl) {
        ValidationLocation savedCurrentLocation = result.getCurrentLocation();
        result.setLocation(new ValidationLocation(context.getManifestURI()));
        try {
            ManifestCms manifest = fetcher.getManifest(context.getManifestURI(), context, result);
            if (manifest == null) {
                return null;
            }

            manifest.validate(context.getManifestURI().toString(), context, new CrlLocator() {
                @Override
                public X509Crl getCrl(URI uri, CertificateRepositoryObjectValidationContext context,
                        ValidationResult result) {
                    Validate.isTrue(uri.equals(crlUri));
                    return crl;
                }
            }, options, result);
            if (result.hasFailureForCurrentLocation()) {
                return null;
            }
            return manifest;
        } finally {
            result.setLocation(savedCurrentLocation);
        }
    }

}