8159393: jlink should print a warning that a signed modular JAR will be treated as unsigned

Reviewed-by: alanb, sundar, mullan, weijun
This commit is contained in:
Jim Laskey 2016-11-07 13:10:42 -04:00
parent 2a0b3e1f75
commit d96b3cd0cb
5 changed files with 275 additions and 62 deletions

View File

@ -359,9 +359,9 @@ class JImageTask {
if (name.endsWith(".class") && !name.endsWith("module-info.class")) {
try {
byte[] bytes = reader.getResource(location);
ClassReader cr =new ClassReader(bytes);
ClassReader cr = new ClassReader(bytes);
ClassNode cn = new ClassNode();
cr.accept(cn, ClassReader.EXPAND_FRAMES);
cr.accept(cn, 0);
} catch (Exception ex) {
log.println("Error(s) in Class: " + name);
}

View File

@ -33,7 +33,6 @@ import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.lang.module.ResolutionException;
import java.lang.module.ResolvedModule;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.nio.ByteOrder;
import java.nio.file.Files;
@ -41,6 +40,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.tools.jlink.internal.TaskHelper.BadArgs;
import static jdk.tools.jlink.internal.TaskHelper.JLINK_BUNDLE;
@ -62,20 +62,8 @@ import jdk.internal.misc.SharedSecrets;
public class JlinkTask {
static final boolean DEBUG = Boolean.getBoolean("jlink.debug");
private static <T extends Throwable> void fail(Class<T> type,
String format,
Object... args) throws T {
String msg = new Formatter().format(format, args).toString();
try {
T t = type.getConstructor(String.class).newInstance(msg);
throw t;
} catch (InstantiationException |
InvocationTargetException |
NoSuchMethodException |
IllegalAccessException e) {
throw new InternalError("Unable to create an instance of " + type, e);
}
}
// jlink API ignores by default. Remove when signing is implemented.
static final boolean IGNORE_SIGNING_DEFAULT = true;
private static final TaskHelper taskHelper
= new TaskHelper(JLINK_BUNDLE);
@ -143,7 +131,10 @@ public class JlinkTask {
}, "--save-opts"),
new Option<JlinkTask>(false, (task, opt, arg) -> {
task.options.fullVersion = true;
}, true, "--full-version"),};
}, true, "--full-version"),
new Option<JlinkTask>(false, (task, opt, arg) -> {
task.options.ignoreSigning = true;
}, true, "--ignore-signing-information"),};
private static final String PROGNAME = "jlink";
private final OptionsValues options = new OptionsValues();
@ -160,7 +151,8 @@ public class JlinkTask {
/**
* Result codes.
*/
static final int EXIT_OK = 0, // Completed with no errors.
static final int
EXIT_OK = 0, // Completed with no errors.
EXIT_ERROR = 1, // Completed but reported errors.
EXIT_CMDERR = 2, // Bad command-line arguments
EXIT_SYSERR = 3, // System error or resource exhaustion.
@ -171,12 +163,13 @@ public class JlinkTask {
String saveoptsfile;
boolean version;
boolean fullVersion;
List<Path> modulePath = new ArrayList<>();
Set<String> limitMods = new HashSet<>();
Set<String> addMods = new HashSet<>();
final List<Path> modulePath = new ArrayList<>();
final Set<String> limitMods = new HashSet<>();
final Set<String> addMods = new HashSet<>();
Path output;
Path packagedModulesPath;
ByteOrder endian = ByteOrder.nativeOrder();
boolean ignoreSigning = false;
}
int run(String[] args) {
@ -199,7 +192,7 @@ public class JlinkTask {
return EXIT_OK;
}
if (taskHelper.getExistingImage() == null) {
if (options.modulePath == null || options.modulePath.isEmpty()) {
if (options.modulePath.isEmpty()) {
throw taskHelper.newBadArgs("err.modulepath.must.be.specified").showUsage(true);
}
createImage();
@ -248,20 +241,25 @@ public class JlinkTask {
plugins = plugins == null ? new PluginsConfiguration() : plugins;
if (config.getModulepaths().isEmpty()) {
throw new Exception("Empty module paths");
throw new IllegalArgumentException("Empty module paths");
}
ModuleFinder finder = newModuleFinder(config.getModulepaths(),
config.getLimitmods(),
config.getModules());
if (config.getModules().isEmpty()) {
throw new IllegalArgumentException("No modules to add");
}
// First create the image provider
ImageProvider imageProvider
= createImageProvider(finder,
checkAddMods(config.getModules()),
config.getLimitmods(),
config.getByteOrder(),
null);
ImageProvider imageProvider =
createImageProvider(finder,
config.getModules(),
config.getLimitmods(),
config.getByteOrder(),
null,
IGNORE_SIGNING_DEFAULT);
// Then create the Plugin Stack
ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins);
@ -299,19 +297,17 @@ public class JlinkTask {
}
ModuleFinder finder
= newModuleFinder(options.modulePath, options.limitMods, options.addMods);
try {
options.addMods = checkAddMods(options.addMods);
} catch (IllegalArgumentException ex) {
if (options.addMods.isEmpty()) {
throw taskHelper.newBadArgs("err.mods.must.be.specified", "--add-modules")
.showUsage(true);
}
// First create the image provider
ImageProvider imageProvider
= createImageProvider(finder,
ImageProvider imageProvider = createImageProvider(finder,
options.addMods,
options.limitMods,
options.endian,
options.packagedModulesPath);
options.packagedModulesPath,
options.ignoreSigning);
// Then create the Plugin Stack
ImagePluginStack stack = ImagePluginConfiguration.
@ -321,13 +317,6 @@ public class JlinkTask {
stack.operate(imageProvider);
}
private static Set<String> checkAddMods(Set<String> addMods) {
if (addMods.isEmpty()) {
throw new IllegalArgumentException("no modules to add");
}
return addMods;
}
/**
* Returns a module finder to find the observable modules specified in
* the --module-path and --limit-modules options
@ -343,7 +332,7 @@ public class JlinkTask {
return finder;
}
/**
/*
* Returns a module finder of the given module path that limits
* the observable modules to those in the transitive closure of
* the modules specified in {@code limitMods} plus other modules
@ -376,7 +365,8 @@ public class JlinkTask {
Set<String> addMods,
Set<String> limitMods,
ByteOrder order,
Path retainModulesPath)
Path retainModulesPath,
boolean ignoreSigning)
throws IOException
{
if (addMods.isEmpty()) {
@ -390,10 +380,10 @@ public class JlinkTask {
Map<String, Path> mods = cf.modules().stream()
.collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation));
return new ImageHelper(cf, mods, order, retainModulesPath);
return new ImageHelper(cf, mods, order, retainModulesPath, ignoreSigning);
}
/**
/*
* Returns a ModuleFinder that limits observability to the given root
* modules, their transitive dependences, plus a set of other modules.
*/
@ -477,36 +467,57 @@ public class JlinkTask {
}
private static class ImageHelper implements ImageProvider {
final Set<Archive> archives;
final ByteOrder order;
final Path packagedModulesPath;
final boolean ignoreSigning;
final Set<Archive> archives;
ImageHelper(Configuration cf,
Map<String, Path> modsPaths,
ByteOrder order,
Path packagedModulesPath) throws IOException {
archives = modsPaths.entrySet().stream()
.map(e -> newArchive(e.getKey(), e.getValue()))
.collect(Collectors.toSet());
Path packagedModulesPath,
boolean ignoreSigning) throws IOException {
this.order = order;
this.packagedModulesPath = packagedModulesPath;
this.ignoreSigning = ignoreSigning;
this.archives = modsPaths.entrySet().stream()
.map(e -> newArchive(e.getKey(), e.getValue()))
.collect(Collectors.toSet());
}
private Archive newArchive(String module, Path path) {
if (path.toString().endsWith(".jmod")) {
return new JmodArchive(module, path);
} else if (path.toString().endsWith(".jar")) {
return new ModularJarArchive(module, path);
ModularJarArchive modularJarArchive = new ModularJarArchive(module, path);
Stream<Archive.Entry> signatures = modularJarArchive.entries().filter((entry) -> {
String name = entry.name().toUpperCase(Locale.ENGLISH);
return name.startsWith("META-INF/") && name.indexOf('/', 9) == -1 && (
name.endsWith(".SF") ||
name.endsWith(".DSA") ||
name.endsWith(".RSA") ||
name.endsWith(".EC") ||
name.startsWith("META-INF/SIG-")
);
});
if (signatures.count() != 0) {
if (ignoreSigning) {
System.err.println(taskHelper.getMessage("warn.signing", path));
} else {
throw new IllegalArgumentException(taskHelper.getMessage("err.signing", path));
}
}
return modularJarArchive;
} else if (Files.isDirectory(path)) {
return new DirArchive(path);
} else {
fail(RuntimeException.class,
"Selected module %s (%s) not in jmod or modular jar format",
module,
path);
throw new IllegalArgumentException(
taskHelper.getMessage("err.not.modular.format", module, path));
}
return null;
}
@Override

View File

@ -212,7 +212,7 @@ public final class TaskHelper {
mainOptions.add(new PlugOption(true, (task, opt, arg) -> {
Path path = Paths.get(arg);
if (!Files.exists(path) || !Files.isDirectory(path)) {
throw newBadArgs("err.existing.image.must.exist");
throw newBadArgs("err.image.must.exist");
}
existingImage = path.toAbsolutePath();
}, true, POST_PROCESS));

View File

@ -62,6 +62,9 @@ main.opt.endian=\
main.opt.save-opts=\
\ --save-opts <filename> Save jlink options in the given file
main.opt.ignore-signing-information=\
\ --ignore-signing-information Ignore signing information in modular JARs
main.msg.bug=\
An exception has occurred in jlink. \
Please file a bug at the Java Bug Database (http://bugreport.java.com/bugreport/) \
@ -88,7 +91,7 @@ err.modulepath.must.be.specified:--module-path must be specified
err.mods.must.be.specified:no modules specified to {0}
err.path.not.found=path not found: {0}
err.path.not.valid=invalid path: {0}
err.existing.image.must.exist=existing image doesn't exists or is not a directory
err.image.must.exist=image does not exist or is not a directory
err.existing.image.invalid=existing image is not valid
err.file.not.found=cannot find file: {0}
err.file.error=cannot access file: {0}
@ -104,5 +107,9 @@ err.option.unsupported={0} not supported: {1}
err.config.defaults=property {0} is missing from configuration
err.config.defaults.value=wrong value in defaults property: {0}
err.bom.generation=bom file generation failed: {0}
warn.invalid.arg=Invalid classname or pathname not exist: {0}
err.not.modular.format=selected module {0} ({1}) not in jmod or modular JAR format
err.signing=signed modular JAR {0} is currently not supported,\
\ use --ignore-signing-information to suppress error
warn.signing=signed modular JAR {0} is currently not supported
warn.invalid.arg=invalid classname or pathname not exist: {0}
warn.split.package=package {0} defined in {1} {2}

View File

@ -0,0 +1,195 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8159393
* @summary Test signed jars involved in image creation
* @modules java.base/jdk.internal.jimage
* jdk.jlink/jdk.tools.jlink.internal
* jdk.compiler/com.sun.tools.javac
* java.base/sun.security.tools.keytool
* jdk.jartool/sun.security.tools.jarsigner
* jdk.jartool/sun.tools.jar
* @run main/othervm JLinkSigningTest
*/
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
public class JLinkSigningTest {
static final String[] MODULE_INFO = {
"module test {",
"}",
};
static final String[] TEST_CLASS = {
"package test;",
"public class test {",
" public static void main(String[] args) {",
" }",
"}",
};
static void report(String command, String[] args) {
System.out.println(command + " " + String.join(" ", Arrays.asList(args)));
}
static void javac(String[] args) {
report("javac", args);
com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main();
if (javac.compile(args) != 0) {
throw new RuntimeException("javac failed");
}
}
static void jar(String[] args) {
report("jar", args);
sun.tools.jar.Main jar = new sun.tools.jar.Main(System.out, System.err, "jar");
if (!jar.run(args)) {
throw new RuntimeException("jar failed");
}
}
static void keytool(String[] args) {
report("keytool", args);
try {
sun.security.tools.keytool.Main.main(args);
} catch (Exception ex) {
throw new RuntimeException("keytool failed");
}
}
static void jarsigner(String[] args) {
report("jarsigner", args);
try {
sun.security.tools.jarsigner.Main.main(args);
} catch (Exception ex) {
throw new RuntimeException("jarsigner failed");
}
}
static void jlink(String[] args) {
report("jlink", args);
try {
jdk.tools.jlink.internal.Main.run(new PrintWriter(System.out, true),
new PrintWriter(System.err, true),
args);
} catch (Exception ex) {
throw new RuntimeException("jlink failed");
}
}
public static void main(String[] args) {
final String JAVA_HOME = System.getProperty("java.home");
Path moduleInfoJavaPath = Paths.get("module-info.java");
Path moduleInfoClassPath = Paths.get("module-info.class");
Path testDirectoryPath = Paths.get("test");
Path testJavaPath = testDirectoryPath.resolve("test.java");
Path testClassPath = testDirectoryPath.resolve("test.class");
Path testModsDirectoryPath = Paths.get("testmods");
Path jmodsPath = Paths.get(JAVA_HOME, "jmods");
Path testjarPath = testModsDirectoryPath.resolve("test.jar");
String modulesPath = testjarPath.toString() +
File.pathSeparator +
jmodsPath.toString();
try {
Files.write(moduleInfoJavaPath, Arrays.asList(MODULE_INFO));
Files.createDirectories(testDirectoryPath);
Files.write(testJavaPath, Arrays.asList(TEST_CLASS));
Files.createDirectories(testModsDirectoryPath);
} catch (IOException ex) {
throw new RuntimeException("file construction failed");
}
javac(new String[] {
testJavaPath.toString(),
moduleInfoJavaPath.toString(),
});
jar(new String[] {
"-c",
"-f", testjarPath.toString(),
"--module-path", jmodsPath.toString(),
testClassPath.toString(),
moduleInfoClassPath.toString(),
});
keytool(new String[] {
"-genkey",
"-keyalg", "RSA",
"-dname", "CN=John Doe, OU=JPG, O=Oracle, L=Santa Clara, ST=California, C=US",
"-alias", "examplekey",
"-storepass", "password",
"-keypass", "password",
"-keystore", "examplekeystore",
"-validity", "365",
});
jarsigner(new String[] {
"-keystore", "examplekeystore",
"-verbose", testjarPath.toString(),
"-storepass", "password",
"-keypass", "password",
"examplekey",
});
try {
jlink(new String[] {
"--module-path", modulesPath,
"--add-modules", "test",
"--output", "foo",
});
} catch (Throwable ex) {
System.out.println("Failed as should");
}
try {
jlink(new String[] {
"--module-path", modulesPath,
"--add-modules", "test",
"--ignore-signing-information",
"--output", "foo",
});
System.out.println("Suceeded as should");
} catch (Throwable ex) {
System.err.println("Should not have failed");
throw new RuntimeException(ex);
}
System.out.println("Done");
}
}