mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8194534: Manifest better support
Reviewed-by: mchung, igerasim
This commit is contained in:
parent
cd8e70a35c
commit
a58b68027b
8 changed files with 105 additions and 18 deletions
|
@ -535,13 +535,13 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
|
||||||
* @spec JPMS
|
* @spec JPMS
|
||||||
*/
|
*/
|
||||||
protected Package definePackage(String name, Manifest man, URL url) {
|
protected Package definePackage(String name, Manifest man, URL url) {
|
||||||
String path = name.replace('.', '/').concat("/");
|
|
||||||
String specTitle = null, specVersion = null, specVendor = null;
|
String specTitle = null, specVersion = null, specVendor = null;
|
||||||
String implTitle = null, implVersion = null, implVendor = null;
|
String implTitle = null, implVersion = null, implVendor = null;
|
||||||
String sealed = null;
|
String sealed = null;
|
||||||
URL sealBase = null;
|
URL sealBase = null;
|
||||||
|
|
||||||
Attributes attr = man.getAttributes(path);
|
Attributes attr = SharedSecrets.javaUtilJarAccess()
|
||||||
|
.getTrustedAttributes(man, name.replace('.', '/').concat("/"));
|
||||||
if (attr != null) {
|
if (attr != null) {
|
||||||
specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
|
specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
|
||||||
specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
|
specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
|
||||||
|
@ -585,10 +585,12 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
|
||||||
/*
|
/*
|
||||||
* Returns true if the specified package name is sealed according to the
|
* Returns true if the specified package name is sealed according to the
|
||||||
* given manifest.
|
* given manifest.
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the package name is untrusted in the manifest
|
||||||
*/
|
*/
|
||||||
private boolean isSealed(String name, Manifest man) {
|
private boolean isSealed(String name, Manifest man) {
|
||||||
String path = name.replace('.', '/').concat("/");
|
Attributes attr = SharedSecrets.javaUtilJarAccess()
|
||||||
Attributes attr = man.getAttributes(path);
|
.getTrustedAttributes(man, name.replace('.', '/').concat("/"));
|
||||||
String sealed = null;
|
String sealed = null;
|
||||||
if (attr != null) {
|
if (attr != null) {
|
||||||
sealed = attr.getValue(Name.SEALED);
|
sealed = attr.getValue(Name.SEALED);
|
||||||
|
|
|
@ -417,10 +417,10 @@ class JarFile extends ZipFile {
|
||||||
if (manEntry != null) {
|
if (manEntry != null) {
|
||||||
if (verify) {
|
if (verify) {
|
||||||
byte[] b = getBytes(manEntry);
|
byte[] b = getBytes(manEntry);
|
||||||
man = new Manifest(new ByteArrayInputStream(b), getName());
|
|
||||||
if (!jvInitialized) {
|
if (!jvInitialized) {
|
||||||
jv = new JarVerifier(b);
|
jv = new JarVerifier(b);
|
||||||
}
|
}
|
||||||
|
man = new Manifest(jv, new ByteArrayInputStream(b), getName());
|
||||||
} else {
|
} else {
|
||||||
man = new Manifest(super.getInputStream(manEntry), getName());
|
man = new Manifest(super.getInputStream(manEntry), getName());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -866,4 +866,24 @@ class JarVerifier {
|
||||||
static CodeSource getUnsignedCS(URL url) {
|
static CodeSource getUnsignedCS(URL url) {
|
||||||
return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null);
|
return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the name is trusted. Used by
|
||||||
|
* {@link Manifest#getTrustedAttributes(String)}.
|
||||||
|
*/
|
||||||
|
boolean isTrustedManifestEntry(String name) {
|
||||||
|
// How many signers? MANIFEST.MF is always verified
|
||||||
|
CodeSigner[] forMan = verifiedSigners.get(JarFile.MANIFEST_NAME);
|
||||||
|
if (forMan == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Check sigFileSigners first, because we are mainly dealing with
|
||||||
|
// non-file entries which will stay in sigFileSigners forever.
|
||||||
|
CodeSigner[] forName = sigFileSigners.get(name);
|
||||||
|
if (forName == null) {
|
||||||
|
forName = verifiedSigners.get(name);
|
||||||
|
}
|
||||||
|
// Returns trusted if all signers sign the entry
|
||||||
|
return forName != null && forName.length == forMan.length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -60,4 +60,9 @@ class JavaUtilJarAccessImpl implements JavaUtilJarAccess {
|
||||||
public List<Object> getManifestDigests(JarFile jar) {
|
public List<Object> getManifestDigests(JarFile jar) {
|
||||||
return jar.getManifestDigests();
|
return jar.getManifestDigests();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Attributes getTrustedAttributes(Manifest man, String name) {
|
||||||
|
return man.getTrustedAttributes(name);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,10 +50,13 @@ import sun.security.util.SecurityProperties;
|
||||||
public class Manifest implements Cloneable {
|
public class Manifest implements Cloneable {
|
||||||
|
|
||||||
// manifest main attributes
|
// manifest main attributes
|
||||||
private Attributes attr = new Attributes();
|
private final Attributes attr = new Attributes();
|
||||||
|
|
||||||
// manifest entries
|
// manifest entries
|
||||||
private Map<String, Attributes> entries = new HashMap<>();
|
private final Map<String, Attributes> entries = new HashMap<>();
|
||||||
|
|
||||||
|
// associated JarVerifier, not null when called by JarFile::getManifest.
|
||||||
|
private final JarVerifier jv;
|
||||||
|
|
||||||
// name of the corresponding jar archive if available.
|
// name of the corresponding jar archive if available.
|
||||||
private final String jarFilename;
|
private final String jarFilename;
|
||||||
|
@ -63,6 +66,7 @@ public class Manifest implements Cloneable {
|
||||||
*/
|
*/
|
||||||
public Manifest() {
|
public Manifest() {
|
||||||
jarFilename = null;
|
jarFilename = null;
|
||||||
|
jv = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,8 +76,7 @@ public class Manifest implements Cloneable {
|
||||||
* @throws IOException if an I/O error has occurred
|
* @throws IOException if an I/O error has occurred
|
||||||
*/
|
*/
|
||||||
public Manifest(InputStream is) throws IOException {
|
public Manifest(InputStream is) throws IOException {
|
||||||
this();
|
this(null, is, null);
|
||||||
read(is);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,8 +87,17 @@ public class Manifest implements Cloneable {
|
||||||
* @throws IOException if an I/O error has occured
|
* @throws IOException if an I/O error has occured
|
||||||
*/
|
*/
|
||||||
Manifest(InputStream is, String jarFilename) throws IOException {
|
Manifest(InputStream is, String jarFilename) throws IOException {
|
||||||
|
this(null, is, jarFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Manifest from the specified input stream
|
||||||
|
* and associates it with a JarVerifier.
|
||||||
|
*/
|
||||||
|
Manifest(JarVerifier jv, InputStream is, String jarFilename) throws IOException {
|
||||||
read(is);
|
read(is);
|
||||||
this.jarFilename = jarFilename;
|
this.jarFilename = jarFilename;
|
||||||
|
this.jv = jv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,9 +106,10 @@ public class Manifest implements Cloneable {
|
||||||
* @param man the Manifest to copy
|
* @param man the Manifest to copy
|
||||||
*/
|
*/
|
||||||
public Manifest(Manifest man) {
|
public Manifest(Manifest man) {
|
||||||
this();
|
|
||||||
attr.putAll(man.getMainAttributes());
|
attr.putAll(man.getMainAttributes());
|
||||||
entries.putAll(man.getEntries());
|
entries.putAll(man.getEntries());
|
||||||
|
jarFilename = null;
|
||||||
|
jv = man.jv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,6 +159,23 @@ public class Manifest implements Cloneable {
|
||||||
return getEntries().get(name);
|
return getEntries().get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Attributes for the specified entry name, if trusted.
|
||||||
|
*
|
||||||
|
* @param name entry name
|
||||||
|
* @return returns the same result as {@link #getAttributes(String)}
|
||||||
|
* @throws SecurityException if the associated jar is signed but this entry
|
||||||
|
* has been modified after signing (i.e. the section in the manifest
|
||||||
|
* does not exist in SF files of all signers).
|
||||||
|
*/
|
||||||
|
Attributes getTrustedAttributes(String name) {
|
||||||
|
Attributes result = getAttributes(name);
|
||||||
|
if (result != null && jv != null && ! jv.isTrustedManifestEntry(name)) {
|
||||||
|
throw new SecurityException("Untrusted manifest entry: " + name);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the main Attributes as well as the entries in this Manifest.
|
* Clears the main Attributes as well as the entries in this Manifest.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -60,6 +60,7 @@ import java.util.jar.Attributes;
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import jdk.internal.misc.SharedSecrets;
|
||||||
import jdk.internal.misc.VM;
|
import jdk.internal.misc.VM;
|
||||||
import jdk.internal.module.ModulePatcher.PatchedModuleReader;
|
import jdk.internal.module.ModulePatcher.PatchedModuleReader;
|
||||||
import jdk.internal.module.Resources;
|
import jdk.internal.module.Resources;
|
||||||
|
@ -862,7 +863,8 @@ public class BuiltinClassLoader
|
||||||
* Manifest are used to get the package version and sealing information.
|
* Manifest are used to get the package version and sealing information.
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException if the package name duplicates an
|
* @throws IllegalArgumentException if the package name duplicates an
|
||||||
* existing package either in this class loader or one of its ancestors
|
* existing package either in this class loader or one of its ancestors
|
||||||
|
* @throws SecurityException if the package name is untrusted in the manifest
|
||||||
*/
|
*/
|
||||||
private Package definePackage(String pn, Manifest man, URL url) {
|
private Package definePackage(String pn, Manifest man, URL url) {
|
||||||
String specTitle = null;
|
String specTitle = null;
|
||||||
|
@ -875,7 +877,8 @@ public class BuiltinClassLoader
|
||||||
URL sealBase = null;
|
URL sealBase = null;
|
||||||
|
|
||||||
if (man != null) {
|
if (man != null) {
|
||||||
Attributes attr = man.getAttributes(pn.replace('.', '/').concat("/"));
|
Attributes attr = SharedSecrets.javaUtilJarAccess()
|
||||||
|
.getTrustedAttributes(man, pn.replace('.', '/').concat("/"));
|
||||||
if (attr != null) {
|
if (attr != null) {
|
||||||
specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
|
specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
|
||||||
specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
|
specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
|
||||||
|
@ -921,10 +924,12 @@ public class BuiltinClassLoader
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if the specified package name is sealed according to
|
* Returns {@code true} if the specified package name is sealed according to
|
||||||
* the given manifest.
|
* the given manifest.
|
||||||
|
*
|
||||||
|
* @throws SecurityException if the package name is untrusted in the manifest
|
||||||
*/
|
*/
|
||||||
private boolean isSealed(String pn, Manifest man) {
|
private boolean isSealed(String pn, Manifest man) {
|
||||||
String path = pn.replace('.', '/').concat("/");
|
Attributes attr = SharedSecrets.javaUtilJarAccess()
|
||||||
Attributes attr = man.getAttributes(path);
|
.getTrustedAttributes(man, pn.replace('.', '/').concat("/"));
|
||||||
String sealed = null;
|
String sealed = null;
|
||||||
if (attr != null)
|
if (attr != null)
|
||||||
sealed = attr.getValue(Attributes.Name.SEALED);
|
sealed = attr.getValue(Attributes.Name.SEALED);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -30,8 +30,10 @@ import java.net.URL;
|
||||||
import java.security.CodeSource;
|
import java.security.CodeSource;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.jar.Attributes;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
public interface JavaUtilJarAccess {
|
public interface JavaUtilJarAccess {
|
||||||
public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException;
|
public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException;
|
||||||
|
@ -41,4 +43,5 @@ public interface JavaUtilJarAccess {
|
||||||
public Enumeration<JarEntry> entries2(JarFile jar);
|
public Enumeration<JarEntry> entries2(JarFile jar);
|
||||||
public void setEagerValidation(JarFile jar, boolean eager);
|
public void setEagerValidation(JarFile jar, boolean eager);
|
||||||
public List<Object> getManifestDigests(JarFile jar);
|
public List<Object> getManifestDigests(JarFile jar);
|
||||||
|
public Attributes getTrustedAttributes(Manifest man, String name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
package jdk.test.lib.util;
|
package jdk.test.lib.util;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
@ -271,6 +272,11 @@ public final class JarUtils {
|
||||||
changes = new HashMap<>(changes);
|
changes = new HashMap<>(changes);
|
||||||
|
|
||||||
System.out.printf("Creating %s from %s...\n", dest, src);
|
System.out.printf("Creating %s from %s...\n", dest, src);
|
||||||
|
|
||||||
|
if (dest.equals(src)) {
|
||||||
|
throw new IOException("src and dest cannot be the same");
|
||||||
|
}
|
||||||
|
|
||||||
try (JarOutputStream jos = new JarOutputStream(
|
try (JarOutputStream jos = new JarOutputStream(
|
||||||
new FileOutputStream(dest))) {
|
new FileOutputStream(dest))) {
|
||||||
|
|
||||||
|
@ -298,6 +304,22 @@ public final class JarUtils {
|
||||||
System.out.println();
|
System.out.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the Manifest inside a jar.
|
||||||
|
*
|
||||||
|
* @param src the original jar file name
|
||||||
|
* @param dest the new jar file name
|
||||||
|
* @param man the Manifest
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void updateManifest(String src, String dest, Manifest man)
|
||||||
|
throws IOException {
|
||||||
|
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||||
|
man.write(bout);
|
||||||
|
updateJar(src, dest, Map.of(JarFile.MANIFEST_NAME, bout.toByteArray()));
|
||||||
|
}
|
||||||
|
|
||||||
private static void updateEntry(JarOutputStream jos, String name, Object content)
|
private static void updateEntry(JarOutputStream jos, String name, Object content)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (content instanceof Boolean) {
|
if (content instanceof Boolean) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue