8194534: Manifest better support

Reviewed-by: mchung, igerasim
This commit is contained in:
Weijun Wang 2018-04-17 15:55:49 +08:00
parent cd8e70a35c
commit a58b68027b
8 changed files with 105 additions and 18 deletions

View file

@ -417,10 +417,10 @@ class JarFile extends ZipFile {
if (manEntry != null) {
if (verify) {
byte[] b = getBytes(manEntry);
man = new Manifest(new ByteArrayInputStream(b), getName());
if (!jvInitialized) {
jv = new JarVerifier(b);
}
man = new Manifest(jv, new ByteArrayInputStream(b), getName());
} else {
man = new Manifest(super.getInputStream(manEntry), getName());
}

View file

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -866,4 +866,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;
}
}

View file

@ -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.
*
* 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);
}
}

View file

@ -50,10 +50,13 @@ import sun.security.util.SecurityProperties;
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;
// name of the corresponding jar archive if available.
private final String jarFilename;
@ -63,6 +66,7 @@ public class Manifest implements Cloneable {
*/
public Manifest() {
jarFilename = null;
jv = null;
}
/**
@ -72,8 +76,7 @@ public class Manifest implements Cloneable {
* @throws IOException if an I/O error has occurred
*/
public Manifest(InputStream is) throws IOException {
this();
read(is);
this(null, is, null);
}
/**
@ -84,8 +87,17 @@ public class Manifest implements Cloneable {
* @throws IOException if an I/O error has occured
*/
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);
this.jarFilename = jarFilename;
this.jv = jv;
}
/**
@ -94,9 +106,10 @@ public class Manifest implements Cloneable {
* @param man the Manifest to copy
*/
public Manifest(Manifest man) {
this();
attr.putAll(man.getMainAttributes());
entries.putAll(man.getEntries());
jarFilename = null;
jv = man.jv;
}
/**
@ -146,6 +159,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.
*/