| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.apache.tools.ant.taskdefs; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.Reader; |
| |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.filters.ChainableReader; |
| import org.apache.tools.ant.types.FilterChain; |
| import org.apache.tools.ant.types.Path; |
| import org.apache.tools.ant.types.RedirectorElement; |
| import org.apache.tools.ant.types.Resource; |
| import org.apache.tools.ant.types.resources.FileProvider; |
| |
| /** |
| * JAR verification task. |
| * For every JAR passed in, we fork jarsigner to verify |
| * that it is correctly signed. This is more rigorous than just checking for |
| * the existence of a signature; the entire certification chain is tested |
| * @since Ant 1.7 |
| */ |
| |
| public class VerifyJar extends AbstractJarSignerTask { |
| /** |
| * no file message {@value} |
| */ |
| public static final String ERROR_NO_FILE = "Not found :"; |
| |
| /** Error output if there is a failure to verify the jar. */ |
| public static final String ERROR_NO_VERIFY = "Failed to verify "; |
| |
| /** |
| * The string we look for in the text to indicate direct verification |
| */ |
| private static final String VERIFIED_TEXT = "jar verified."; |
| |
| /** |
| * certification flag |
| */ |
| private boolean certificates = false; |
| private BufferingOutputFilter outputCache = new BufferingOutputFilter(); |
| |
| private String savedStorePass = null; |
| |
| /** |
| * Ask for certificate information to be printed |
| * @param certificates if true print certificates. |
| */ |
| public void setCertificates(boolean certificates) { |
| this.certificates = certificates; |
| } |
| |
| /** |
| * verify our jar files |
| * @throws BuildException on error. |
| */ |
| @Override |
| public void execute() throws BuildException { |
| //validation logic |
| final boolean hasJar = jar != null; |
| |
| if (!hasJar && !hasResources()) { |
| throw new BuildException(ERROR_NO_SOURCE); |
| } |
| |
| beginExecution(); |
| |
| //patch the redirector to save output to a file |
| RedirectorElement redirector = getRedirector(); |
| redirector.setAlwaysLog(true); |
| FilterChain outputFilterChain = redirector.createOutputFilterChain(); |
| outputFilterChain.add(outputCache); |
| |
| try { |
| Path sources = createUnifiedSourcePath(); |
| for (Resource r : sources) { |
| FileProvider fr = r.as(FileProvider.class); |
| verifyOneJar(fr.getFile()); |
| } |
| } finally { |
| endExecution(); |
| } |
| } |
| |
| /** |
| * @since 1.10.3 |
| */ |
| @Override |
| protected void beginExecution() { |
| // when using a PKCS12 keystore jarsigner -verify will not |
| // prompt for the keystore password but will only properly |
| // verify the jar with -strict enabled if the -storepass |
| // parameter is used. Note that the documentation of jarsigner |
| // says -storepass was never required with -verify - this is |
| // wrong. |
| // |
| // See https://bz.apache.org/bugzilla/show_bug.cgi?id=62194 |
| // |
| // So if strict is true then we hide storepass from the base |
| // implementation and instead add the -storepass command line |
| // argument |
| if (storepass != null) { |
| savedStorePass = storepass; |
| setStorepass(null); |
| } |
| super.beginExecution(); |
| } |
| |
| /** |
| * @since 1.10.3 |
| */ |
| @Override |
| protected void endExecution() { |
| if (savedStorePass != null) { |
| setStorepass(savedStorePass); |
| savedStorePass = null; |
| } |
| super.endExecution(); |
| } |
| |
| /** |
| * verify a JAR. |
| * @param jar the jar to verify. |
| * @throws BuildException if the file could not be verified |
| */ |
| private void verifyOneJar(File jar) { |
| if (!jar.exists()) { |
| throw new BuildException(ERROR_NO_FILE + jar); |
| } |
| final ExecTask cmd = createJarSigner(); |
| |
| setCommonOptions(cmd); |
| bindToKeystore(cmd); |
| if (savedStorePass != null) { |
| addValue(cmd, "-storepass"); |
| addValue(cmd, savedStorePass); |
| } |
| |
| //verify special operations |
| addValue(cmd, "-verify"); |
| |
| if (certificates) { |
| addValue(cmd, "-certs"); |
| } |
| |
| //JAR is required |
| addValue(cmd, jar.getPath()); |
| |
| if (alias != null) { |
| addValue(cmd, alias); |
| } |
| |
| log("Verifying JAR: " + jar.getAbsolutePath()); |
| outputCache.clear(); |
| BuildException ex = null; |
| try { |
| cmd.execute(); |
| } catch (BuildException e) { |
| ex = e; |
| } |
| String results = outputCache.toString(); |
| //deal with jdk1.4.2 bug: |
| if (ex != null) { |
| if (results.contains("zip file closed")) { |
| log("You are running " + JARSIGNER_COMMAND |
| + " against a JVM with a known bug that manifests as an IllegalStateException.", |
| Project.MSG_WARN); |
| } else { |
| throw ex; |
| } |
| } |
| if (!results.contains(VERIFIED_TEXT)) { |
| throw new BuildException(ERROR_NO_VERIFY + jar); |
| } |
| } |
| |
| /** |
| * we are not thread safe here. Do not use on multiple threads at the same time. |
| */ |
| private static class BufferingOutputFilter implements ChainableReader { |
| |
| private BufferingOutputFilterReader buffer; |
| |
| @Override |
| public Reader chain(Reader rdr) { |
| buffer = new BufferingOutputFilterReader(rdr); |
| return buffer; |
| } |
| |
| @Override |
| public String toString() { |
| return buffer.toString(); |
| } |
| |
| public void clear() { |
| if (buffer != null) { |
| buffer.clear(); |
| } |
| } |
| } |
| |
| /** |
| * catch the output of the buffer |
| */ |
| private static class BufferingOutputFilterReader extends Reader { |
| |
| private Reader next; |
| |
| private StringBuffer buffer = new StringBuffer(); |
| |
| public BufferingOutputFilterReader(Reader next) { |
| this.next = next; |
| } |
| |
| @Override |
| public int read(char[] cbuf, int off, int len) throws IOException { |
| //hand down |
| int result = next.read(cbuf, off, len); |
| //cache |
| buffer.append(cbuf, off, len); |
| //return |
| return result; |
| } |
| |
| @Override |
| public void close() throws IOException { |
| next.close(); |
| } |
| |
| @Override |
| public String toString() { |
| return buffer.toString(); |
| } |
| |
| public void clear() { |
| buffer = new StringBuffer(); |
| } |
| } |
| } |