001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs;
020:
021: import org.apache.tools.ant.Project;
022: import org.apache.tools.ant.BuildException;
023: import org.apache.tools.ant.filters.ChainableReader;
024: import org.apache.tools.ant.types.RedirectorElement;
025: import org.apache.tools.ant.types.FilterChain;
026: import org.apache.tools.ant.types.Path;
027: import org.apache.tools.ant.types.resources.FileResource;
028:
029: import java.util.Iterator;
030: import java.io.File;
031: import java.io.Reader;
032: import java.io.IOException;
033:
034: /**
035: * JAR verification task.
036: * For every JAR passed in, we fork jarsigner to verify
037: * that it is correctly signed. This is more rigorous than just checking for
038: * the existence of a signature; the entire certification chain is tested
039: * @since Ant 1.7
040: */
041:
042: public class VerifyJar extends AbstractJarSignerTask {
043: /**
044: * no file message {@value}
045: */
046: public static final String ERROR_NO_FILE = "Not found :";
047:
048: /**
049: * The string we look for in the text to indicate direct verification
050: */
051: private static final String VERIFIED_TEXT = "jar verified.";
052:
053: /**
054: * certification flag
055: */
056: private boolean certificates = false;
057: private BufferingOutputFilter outputCache = new BufferingOutputFilter();
058: /** Error output if there is a failure to verify the jar. */
059: public static final String ERROR_NO_VERIFY = "Failed to verify ";
060:
061: /**
062: * Ask for certificate information to be printed
063: * @param certificates if true print certificates.
064: */
065: public void setCertificates(boolean certificates) {
066: this .certificates = certificates;
067: }
068:
069: /**
070: * verify our jar files
071: * @throws BuildException on error.
072: */
073: public void execute() throws BuildException {
074: //validation logic
075: final boolean hasJar = jar != null;
076:
077: if (!hasJar && !hasResources()) {
078: throw new BuildException(ERROR_NO_SOURCE);
079: }
080:
081: beginExecution();
082:
083: //patch the redirector to save output to a file
084: RedirectorElement redirector = getRedirector();
085: redirector.setAlwaysLog(true);
086: FilterChain outputFilterChain = redirector
087: .createOutputFilterChain();
088: outputFilterChain.add(outputCache);
089:
090: try {
091: Path sources = createUnifiedSourcePath();
092: Iterator iter = sources.iterator();
093: while (iter.hasNext()) {
094: FileResource fr = (FileResource) iter.next();
095: verifyOneJar(fr.getFile());
096: }
097:
098: } finally {
099: endExecution();
100: }
101:
102: }
103:
104: /**
105: * verify a JAR.
106: * @param jar the jar to verify.
107: * @throws BuildException if the file could not be verified
108: */
109: private void verifyOneJar(File jar) {
110: if (!jar.exists()) {
111: throw new BuildException(ERROR_NO_FILE + jar);
112: }
113: final ExecTask cmd = createJarSigner();
114:
115: setCommonOptions(cmd);
116: bindToKeystore(cmd);
117:
118: //verify special operations
119: addValue(cmd, "-verify");
120:
121: if (certificates) {
122: addValue(cmd, "-certs");
123: }
124:
125: //JAR is required
126: addValue(cmd, jar.getPath());
127:
128: log("Verifying JAR: " + jar.getAbsolutePath());
129: outputCache.clear();
130: BuildException ex = null;
131: try {
132: cmd.execute();
133: } catch (BuildException e) {
134: ex = e;
135: }
136: String results = outputCache.toString();
137: //deal with jdk1.4.2 bug:
138: if (ex != null) {
139: if (results.indexOf("zip file closed") >= 0) {
140: log(
141: "You are running "
142: + JARSIGNER_COMMAND
143: + " against a JVM with"
144: + " a known bug that manifests as an IllegalStateException.",
145: Project.MSG_WARN);
146: } else {
147: throw ex;
148: }
149: }
150: if (results.indexOf(VERIFIED_TEXT) < 0) {
151: throw new BuildException(ERROR_NO_VERIFY + jar);
152: }
153: }
154:
155: /**
156: * we are not thread safe here. Do not use on multiple threads at the same time.
157: */
158: private static class BufferingOutputFilter implements
159: ChainableReader {
160:
161: private BufferingOutputFilterReader buffer;
162:
163: public Reader chain(Reader rdr) {
164: buffer = new BufferingOutputFilterReader(rdr);
165: return buffer;
166: }
167:
168: public String toString() {
169: return buffer.toString();
170: }
171:
172: public void clear() {
173: if (buffer != null) {
174: buffer.clear();
175: }
176: }
177: }
178:
179: /**
180: * catch the output of the buffer
181: */
182: private static class BufferingOutputFilterReader extends Reader {
183:
184: private Reader next;
185:
186: private StringBuffer buffer = new StringBuffer();
187:
188: public BufferingOutputFilterReader(Reader next) {
189: this .next = next;
190: }
191:
192: public int read(char[] cbuf, int off, int len)
193: throws IOException {
194: //hand down
195: int result = next.read(cbuf, off, len);
196: //cache
197: buffer.append(cbuf, off, len);
198: //return
199: return result;
200: }
201:
202: public void close() throws IOException {
203: next.close();
204: }
205:
206: public String toString() {
207: return buffer.toString();
208: }
209:
210: public void clear() {
211: buffer = new StringBuffer();
212: }
213: }
214: }
|