diff --git a/src/main/java/cpw/mods/jarhandling/SecureJar.java b/src/main/java/cpw/mods/jarhandling/SecureJar.java index 3b3b3f5..9432a2c 100644 --- a/src/main/java/cpw/mods/jarhandling/SecureJar.java +++ b/src/main/java/cpw/mods/jarhandling/SecureJar.java @@ -8,6 +8,8 @@ import java.io.InputStream; import java.io.UncheckedIOException; import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleReader; +import java.lang.module.ModuleReference; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; @@ -83,15 +85,45 @@ static SecureJar from(JarContents contents, JarMetadata metadata) { */ Path getRootPath(); + /** + * All the functions that are necessary to turn a {@link SecureJar} into a module. + */ interface ModuleDataProvider { + /** + * {@return the name of the module} + */ String name(); + + /** + * {@return the descriptor of the module} + */ ModuleDescriptor descriptor(); + + /** + * @see ModuleReference#location() + */ + @Nullable URI uri(); + + /** + * @see ModuleReader#find(String) + */ Optional findFile(String name); + + /** + * @see ModuleReader#open(String) + */ Optional open(final String name); + /** + * {@return the manifest of the jar} + */ Manifest getManifest(); + /** + * {@return the signers if the class name can be verified, or {@code null} otherwise} + */ + @Nullable CodeSigner[] verifyAndGetSigners(String cname, byte[] bytes); } @@ -134,7 +166,11 @@ default Set getPackages() { * @deprecated Obtain via the {@link ModuleDescriptor} of the jar if you really need this. */ @Deprecated(forRemoval = true, since = "2.1.16") - List getProviders(); + default List getProviders() { + return moduleDataProvider().descriptor().provides().stream() + .map(p -> new Provider(p.service(), p.providers())) + .toList(); + } /** * @deprecated Use {@link JarContentsBuilder} and {@link #from(JarContents)} instead. diff --git a/src/main/java/cpw/mods/jarhandling/VirtualJar.java b/src/main/java/cpw/mods/jarhandling/VirtualJar.java new file mode 100644 index 0000000..f6fc84c --- /dev/null +++ b/src/main/java/cpw/mods/jarhandling/VirtualJar.java @@ -0,0 +1,147 @@ +package cpw.mods.jarhandling; + +import cpw.mods.niofs.union.UnionFileSystem; +import cpw.mods.niofs.union.UnionFileSystemProvider; +import org.jetbrains.annotations.Nullable; + +import java.io.InputStream; +import java.lang.module.ModuleDescriptor; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.spi.FileSystemProvider; +import java.security.CodeSigner; +import java.util.Optional; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * Implementation of {@link SecureJar} that does not actually contain any files, + * but still defines packages. + * + *

This can be used by frameworks that generate classes at runtime, in specific packages, + * and need to make a {@link SecureJar}-based module system implementation aware of these packages. + */ +public final class VirtualJar implements SecureJar { + /** + * Creates a new virtual jar. + * + * @param name the name of the virtual jar; will be used as the module name + * @param referencePath a path to an existing directory or jar file, for debugging and display purposes + * (for example a path to the real jar of the caller) + * @param packages the list of packages in this virtual jar + */ + public VirtualJar(String name, Path referencePath, String... packages) { + if (!Files.exists(referencePath)) { + throw new IllegalArgumentException("VirtualJar reference path " + referencePath + " must exist"); + } + + this.moduleDescriptor = ModuleDescriptor.newAutomaticModule(name) + .packages(Set.of(packages)) + .build(); + // Create a dummy file system from the reference path, with a filter that always returns false + this.dummyFileSystem = UFSP.newFileSystem((path, basePath) -> false, referencePath); + } + + // Implementation details below + private static final UnionFileSystemProvider UFSP = (UnionFileSystemProvider) FileSystemProvider.installedProviders() + .stream() + .filter(fsp->fsp.getScheme().equals("union")) + .findFirst() + .orElseThrow(()->new IllegalStateException("Couldn't find UnionFileSystemProvider")); + + private final ModuleDescriptor moduleDescriptor; + private final ModuleDataProvider moduleData = new VirtualJarModuleDataProvider(); + private final UnionFileSystem dummyFileSystem; + private final Manifest manifest = new Manifest(); + + @Override + public ModuleDataProvider moduleDataProvider() { + return moduleData; + } + + @Override + public Path getPrimaryPath() { + return dummyFileSystem.getPrimaryPath(); + } + + @Override + public @Nullable CodeSigner[] getManifestSigners() { + return null; + } + + @Override + public Status verifyPath(Path path) { + return Status.NONE; + } + + @Override + public Status getFileStatus(String name) { + return Status.NONE; + } + + @Override + public @Nullable Attributes getTrustedManifestEntries(String name) { + return null; + } + + @Override + public boolean hasSecurityData() { + return false; + } + + @Override + public String name() { + return moduleDescriptor.name(); + } + + @Override + public Path getPath(String first, String... rest) { + return dummyFileSystem.getPath(first, rest); + } + + @Override + public Path getRootPath() { + return dummyFileSystem.getRoot(); + } + + private class VirtualJarModuleDataProvider implements ModuleDataProvider { + @Override + public String name() { + return VirtualJar.this.name(); + } + + @Override + public ModuleDescriptor descriptor() { + return moduleDescriptor; + } + + @Override + @Nullable + public URI uri() { + return null; + } + + @Override + public Optional findFile(String name) { + return Optional.empty(); + } + + @Override + public Optional open(String name) { + return Optional.empty(); + } + + @Override + public Manifest getManifest() { + return manifest; + } + + @Override + @Nullable + public CodeSigner[] verifyAndGetSigners(String cname, byte[] bytes) { + return null; + } + } +} diff --git a/src/main/java/cpw/mods/jarhandling/impl/Jar.java b/src/main/java/cpw/mods/jarhandling/impl/Jar.java index ee9c3e2..f3827b8 100644 --- a/src/main/java/cpw/mods/jarhandling/impl/Jar.java +++ b/src/main/java/cpw/mods/jarhandling/impl/Jar.java @@ -164,6 +164,7 @@ public Manifest getManifest() { } @Override + @Nullable public CodeSigner[] verifyAndGetSigners(final String cname, final byte[] bytes) { return jar.signingData.verifyAndGetSigners(jar.manifest, cname, bytes); } diff --git a/src/main/java/cpw/mods/jarhandling/impl/JarSigningData.java b/src/main/java/cpw/mods/jarhandling/impl/JarSigningData.java index 1ba3d3a..ac5225a 100644 --- a/src/main/java/cpw/mods/jarhandling/impl/JarSigningData.java +++ b/src/main/java/cpw/mods/jarhandling/impl/JarSigningData.java @@ -94,6 +94,7 @@ private Optional getData(final String name) { return Optional.ofNullable(statusData.get(name)); } + @Nullable synchronized CodeSigner[] verifyAndGetSigners(Manifest manifest, String name, byte[] bytes) { if (!hasSecurityData()) return null; if (statusData.containsKey(name)) return statusData.get(name).signers;