| /******************************************************************************* |
| * Copyright (c) 2000, 2008 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.update.internal.verifier; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.*; |
| import java.util.jar.JarFile; |
| import java.util.zip.ZipException; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.osgi.signedcontent.*; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.update.core.*; |
| import org.eclipse.update.internal.core.Messages; |
| import org.eclipse.update.internal.core.UpdateCore; |
| |
| /** |
| * The JarVerifier will check the integrity of the JAR. |
| * If the Jar is signed and the integrity is validated, |
| * it will check if one of the certificate of each file |
| * is in one of the keystore. |
| * |
| */ |
| |
| public class CertVerifier extends Verifier { |
| |
| private CertVerificationResult result; |
| private boolean acceptUnsignedFiles; |
| private IProgressMonitor monitor; |
| private File jarFile; |
| private SignedContentFactory factory; |
| private List trustedSignerInfos; |
| |
| /* |
| * Default Constructor |
| */ |
| public CertVerifier(SignedContentFactory factory) { |
| this.factory = factory; |
| initialize(); |
| } |
| |
| |
| /* |
| * |
| */ |
| private void initialize() { |
| result = null; |
| acceptUnsignedFiles = false; |
| } |
| |
| /* |
| * init |
| */ |
| private void init(IFeature feature, ContentReference contentRef) throws CoreException { |
| jarFile = null; |
| if (contentRef instanceof JarContentReference) { |
| JarContentReference jarReference = (JarContentReference) contentRef; |
| try { |
| jarFile = jarReference.asFile(); |
| if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL) |
| UpdateCore.debug("Attempting to read JAR file:"+jarFile); //$NON-NLS-1$ |
| |
| // # of entries |
| if (!jarFile.exists()) throw new IOException(); |
| JarFile jar = new JarFile(jarFile); |
| if (jar !=null){ |
| try { |
| jar.close(); |
| } catch (IOException ex) { |
| // unchecked |
| } |
| } |
| } catch (ZipException e){ |
| throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_InvalidJar, (new String[] { jarReference.toString() })), e); |
| } catch (IOException e) { |
| throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_UnableToAccessJar, (new String[] { jarReference.toString() })), e); |
| } |
| } |
| |
| result = new CertVerificationResult(); |
| result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR); |
| result.setResultException(null); |
| result.setFeature(feature); |
| result.setContentReference(contentRef); |
| } |
| |
| /* |
| * @param newMonitor org.eclipse.core.runtime.IProgressMonitor |
| */ |
| private void setMonitor(IProgressMonitor newMonitor) { |
| monitor = newMonitor; |
| } |
| |
| /* |
| * @see IVerifier#verify(IFeature,ContentReference,boolean, InstallMonitor) |
| */ |
| public IVerificationResult verify( |
| IFeature feature, |
| ContentReference reference, |
| boolean isFeatureVerification, |
| InstallMonitor monitor) |
| throws CoreException { |
| |
| if (reference == null) |
| return result; |
| |
| // if parent knows how to verify, ask the parent first |
| if (getParent() != null) { |
| IVerificationResult vr = |
| getParent().verify(feature, reference, isFeatureVerification, monitor); |
| if (vr.getVerificationCode() != IVerificationResult.TYPE_ENTRY_UNRECOGNIZED) |
| return vr; |
| } |
| |
| // the parent couldn't verify |
| setMonitor(monitor); |
| init(feature, reference); |
| result.isFeatureVerification(isFeatureVerification); |
| |
| if (jarFile!=null) { |
| result = verify(jarFile.getAbsolutePath(), reference.getIdentifier()); |
| } else { |
| result.setVerificationCode(IVerificationResult.TYPE_ENTRY_UNRECOGNIZED); |
| } |
| |
| return result; |
| } |
| |
| /* |
| * |
| */ |
| private CertVerificationResult verify(String file, String identifier) { |
| |
| try { |
| SignedContent verifier = factory.getSignedContent(new File(file)); |
| // verify integrity |
| verifyIntegrity(verifier, identifier); |
| |
| //if user already said yes |
| result.alreadySeen(alreadyValidated()); |
| |
| // save the fact the file is not signed, so the user will not be prompted again |
| if (result.getVerificationCode() |
| == IVerificationResult.TYPE_ENTRY_NOT_SIGNED) { |
| acceptUnsignedFiles = true; |
| } |
| |
| } catch (Exception e) { |
| result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR); |
| result.setResultException(e); |
| } |
| |
| if (monitor != null) { |
| monitor.worked(1); |
| if (monitor.isCanceled()) { |
| result.setVerificationCode(IVerificationResult.VERIFICATION_CANCELLED); |
| } |
| } |
| |
| return result; |
| } |
| |
| /* |
| * Verifies the integrity of the JAR |
| */ |
| private void verifyIntegrity(SignedContent verifier, String identifier) { |
| try { |
| if (verifier.isSigned()) { |
| // If the JAR is signed and invalid then mark as corrupted |
| if (hasValidContent(verifier.getSignedEntries())) { |
| result.setSignedContent(verifier); |
| SignerInfo[] signers = verifier.getSignerInfos(); |
| for (int i = 0; i < signers.length; i++) |
| if (signers[i].isTrusted()) { |
| result.setVerificationCode(IVerificationResult.TYPE_ENTRY_SIGNED_RECOGNIZED); |
| break; |
| } |
| if (result.getVerificationCode() != IVerificationResult.TYPE_ENTRY_SIGNED_RECOGNIZED) |
| result.setVerificationCode(IVerificationResult.TYPE_ENTRY_SIGNED_UNRECOGNIZED); |
| } else |
| result.setVerificationCode(IVerificationResult.TYPE_ENTRY_CORRUPTED); |
| } else { |
| result.setVerificationCode(IVerificationResult.TYPE_ENTRY_NOT_SIGNED); |
| return; |
| } |
| } catch (Exception e) { |
| result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR); |
| result.setResultException(e); |
| } |
| } |
| |
| private boolean hasValidContent(SignedContentEntry[] signedEntries) { |
| try { |
| for (int i = 0; i < signedEntries.length; i++) |
| signedEntries[i].verify(); |
| } catch (InvalidContentException e) { |
| return false; |
| } catch (IOException e) { |
| return false; |
| } |
| return true; |
| } |
| |
| |
| /* |
| * |
| */ |
| private boolean alreadyValidated() { |
| int verifyCode = result.getVerificationCode(); |
| if (verifyCode == IVerificationResult.TYPE_ENTRY_NOT_SIGNED) |
| return (acceptUnsignedFiles); |
| if (verifyCode == IVerificationResult.UNKNOWN_ERROR) |
| return false; |
| if (result.getSigners() != null) { //getTrustedCertificates() can't be null as it is lazy initialized |
| Iterator iter = getTrustedInfos().iterator(); |
| SignerInfo[] signers = result.getSigners(); |
| |
| // check if this is not a user accepted certificate for this feature |
| while (iter.hasNext()) { |
| SignerInfo chain = (SignerInfo) iter.next(); |
| for (int i = 0; i < signers.length; i++) |
| if (chain.equals(signers[i])) |
| return true; |
| } |
| |
| // if certificate pair not found in trusted add it for next time |
| for (int i = 0; i < signers.length; i++) { |
| addTrustedSignerInfo(signers[i]); |
| } |
| } |
| |
| return false; |
| } |
| |
| /* |
| * |
| */ |
| private void addTrustedSignerInfo(SignerInfo signer) { |
| if (trustedSignerInfos == null) |
| trustedSignerInfos = new ArrayList(); |
| if (signer != null) |
| trustedSignerInfos.add(signer); |
| } |
| |
| /* |
| * |
| */ |
| private List getTrustedInfos() { |
| if (trustedSignerInfos == null) |
| trustedSignerInfos = new ArrayList(); |
| return trustedSignerInfos; |
| } |
| |
| /** |
| * @see IVerifier#setParent(IVerifier) |
| */ |
| public void setParent(IVerifier parentVerifier) { |
| super.setParent(parentVerifier); |
| initialize(); |
| } |
| |
| } |