提交 c270cf4c 编写于 作者: I igerasim

8194534: Manifest better support

Reviewed-by: mchung, igerasim
上级 97f8d985
/*
* Copyright (c) 1997, 2015, 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.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -50,6 +50,7 @@ import java.util.jar.Attributes.Name;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import sun.misc.Resource;
import sun.misc.SharedSecrets;
import sun.misc.URLClassPath;
import sun.net.www.ParseUtil;
import sun.security.util.SecurityConstants;
......@@ -486,13 +487,13 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
protected Package definePackage(String name, Manifest man, URL url)
throws IllegalArgumentException
{
String path = name.replace('.', '/').concat("/");
String specTitle = null, specVersion = null, specVendor = null;
String implTitle = null, implVersion = null, implVendor = null;
String sealed = null;
URL sealBase = null;
Attributes attr = man.getAttributes(path);
Attributes attr = SharedSecrets.javaUtilJarAccess()
.getTrustedAttributes(man, name.replace('.', '/').concat("/"));
if (attr != null) {
specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
......@@ -536,10 +537,12 @@ public class URLClassLoader extends SecureClassLoader implements Closeable {
/*
* Returns true if the specified package name is sealed according to the
* given manifest.
*
* @throws SecurityException if the package name is untrusted in the manifest
*/
private boolean isSealed(String name, Manifest man) {
String path = name.replace('.', '/').concat("/");
Attributes attr = man.getAttributes(path);
Attributes attr = SharedSecrets.javaUtilJarAccess()
.getTrustedAttributes(man, name.replace('.', '/').concat("/"));
String sealed = null;
if (attr != null) {
sealed = attr.getValue(Name.SEALED);
......
/*
* Copyright (c) 1997, 2013, 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.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -191,10 +191,10 @@ class JarFile extends ZipFile {
if (manEntry != null) {
if (verify) {
byte[] b = getBytes(manEntry);
man = new Manifest(new ByteArrayInputStream(b));
if (!jvInitialized) {
jv = new JarVerifier(b);
}
man = new Manifest(jv, new ByteArrayInputStream(b));
} else {
man = new Manifest(super.getInputStream(manEntry));
}
......
/*
* 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.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -879,4 +879,24 @@ class JarVerifier {
static CodeSource getUnsignedCS(URL url) {
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;
}
}
/*
* 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.
*
* 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) {
return jar.getManifestDigests();
}
public Attributes getTrustedAttributes(Manifest man, String name) {
return man.getTrustedAttributes(name);
}
}
/*
* Copyright (c) 1997, 2013, 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.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -48,15 +48,19 @@ import java.util.Iterator;
*/
public class Manifest implements Cloneable {
// manifest main attributes
private Attributes attr = new Attributes();
private final Attributes attr = new Attributes();
// 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;
/**
* Constructs a new, empty Manifest.
*/
public Manifest() {
jv = null;
}
/**
......@@ -66,7 +70,16 @@ public class Manifest implements Cloneable {
* @throws IOException if an I/O error has occurred
*/
public Manifest(InputStream is) throws IOException {
this(null, is);
}
/**
* Constructs a new Manifest from the specified input stream
* and associates it with a JarVerifier.
*/
Manifest(JarVerifier jv, InputStream is) throws IOException {
read(is);
this.jv = jv;
}
/**
......@@ -77,6 +90,7 @@ public class Manifest implements Cloneable {
public Manifest(Manifest man) {
attr.putAll(man.getMainAttributes());
entries.putAll(man.getEntries());
jv = man.jv;
}
/**
......@@ -126,6 +140,23 @@ public class Manifest implements Cloneable {
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.
*/
......
/*
* 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.
*
* 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.util.Enumeration;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
public interface JavaUtilJarAccess {
public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException;
......@@ -41,4 +43,5 @@ public interface JavaUtilJarAccess {
public Enumeration<JarEntry> entries2(JarFile jar);
public void setEagerValidation(JarFile jar, boolean eager);
public List<Object> getManifestDigests(JarFile jar);
public Attributes getTrustedAttributes(Manifest man, String name);
}
/*
* Copyright (c) 2013, 2018, 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.
*/
package jdk.testlibrary;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.Arrays;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.FileObject;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
/**
* {@code InMemoryJavaCompiler} can be used for compiling a {@link
* CharSequence} to a {@code byte[]}.
*
* The compiler will not use the file system at all, instead using a {@link
* ByteArrayOutputStream} for storing the byte code. For the source code, any
* kind of {@link CharSequence} can be used, e.g. {@link String}, {@link
* StringBuffer} or {@link StringBuilder}.
*
* The {@code InMemoryCompiler} can easily be used together with a {@code
* ByteClassLoader} to easily compile and load source code in a {@link String}:
*
* <pre>
* {@code
* import com.oracle.java.testlibrary.InMemoryJavaCompiler;
* import com.oracle.java.testlibrary.ByteClassLoader;
*
* class Example {
* public static void main(String[] args) {
* String className = "Foo";
* String sourceCode = "public class " + className + " {" +
* " public void bar() {" +
* " System.out.println("Hello from bar!");" +
* " }" +
* "}";
* byte[] byteCode = InMemoryJavaCompiler.compile(className, sourceCode);
* Class fooClass = ByteClassLoader.load(className, byteCode);
* }
* }
* }
* </pre>
*/
public class InMemoryJavaCompiler {
private static class MemoryJavaFileObject extends SimpleJavaFileObject {
private final String className;
private final CharSequence sourceCode;
private final ByteArrayOutputStream byteCode;
public MemoryJavaFileObject(String className, CharSequence sourceCode) {
super(URI.create("string:///" + className.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE);
this.className = className;
this.sourceCode = sourceCode;
this.byteCode = new ByteArrayOutputStream();
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return sourceCode;
}
@Override
public OutputStream openOutputStream() throws IOException {
return byteCode;
}
public byte[] getByteCode() {
return byteCode.toByteArray();
}
public String getClassName() {
return className;
}
}
private static class FileManagerWrapper extends ForwardingJavaFileManager {
private MemoryJavaFileObject file;
public FileManagerWrapper(MemoryJavaFileObject file) {
super(getCompiler().getStandardFileManager(null, null, null));
this.file = file;
}
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className,
Kind kind, FileObject sibling)
throws IOException {
if (!file.getClassName().equals(className)) {
throw new IOException("Expected class with name " + file.getClassName() +
", but got " + className);
}
return file;
}
}
/**
* Compiles the class with the given name and source code.
*
* @param className The name of the class
* @param sourceCode The source code for the class with name {@code className}
* @throws RuntimeException if the compilation did not succeed
* @return The resulting byte code from the compilation
*/
public static byte[] compile(String className, CharSequence sourceCode) {
MemoryJavaFileObject file = new MemoryJavaFileObject(className, sourceCode);
CompilationTask task = getCompilationTask(file);
if(!task.call()) {
throw new RuntimeException("Could not compile " + className + " with source code " + sourceCode);
}
return file.getByteCode();
}
private static JavaCompiler getCompiler() {
return ToolProvider.getSystemJavaCompiler();
}
private static CompilationTask getCompilationTask(MemoryJavaFileObject file) {
return getCompiler().getTask(null, new FileManagerWrapper(file), null, null, null, Arrays.asList(file));
}
}
/*
* Copyright (c) 2015, 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.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -23,6 +23,7 @@
package jdk.testlibrary;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
......@@ -126,6 +127,11 @@ public final class JarUtils {
changes = new HashMap<>(changes);
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(
new FileOutputStream(dest))) {
......@@ -153,6 +159,24 @@ public final class JarUtils {
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);
Map<String, Object> map = new HashMap<>();
map.put(JarFile.MANIFEST_NAME, bout.toByteArray());
updateJar(src, dest, map);
}
private static void updateEntry(JarOutputStream jos, String name, Object content)
throws IOException {
if (content instanceof Boolean) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册