6742654: Code insertion/replacement attacks against signed jars
6911041: JCK api/signaturetest tests fails for Mixed Code PIT builds (b91) for all trains 6921823: JarVerifier csdomain field not initialized 6921839: Update trusted.libraries list Reviewed-by: dgu
This commit is contained in:
parent
0e63901156
commit
d43bb4114c
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 1996, 2010 Oracle and/or its affiliates. All rights reserved.
|
# Copyright (c) 1996, 2011 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
|
||||||
@ -65,6 +65,8 @@ CACERTS_BUILD = $(LIBDIR)/security/cacerts
|
|||||||
ifndef OPENJDK
|
ifndef OPENJDK
|
||||||
BLACKLIST_SRC = $(CLOSED_SHARE_SRC)/lib/security/blacklist
|
BLACKLIST_SRC = $(CLOSED_SHARE_SRC)/lib/security/blacklist
|
||||||
BLACKLIST_BUILD = $(LIBDIR)/security/blacklist
|
BLACKLIST_BUILD = $(LIBDIR)/security/blacklist
|
||||||
|
TRUSTEDLIBS_SRC = $(CLOSED_SHARE_SRC)/lib/security/trusted.libraries
|
||||||
|
TRUSTEDLIBS_BUILD = $(LIBDIR)/security/trusted.libraries
|
||||||
endif
|
endif
|
||||||
|
|
||||||
FILES_class = $(FILES_java:%.java=$(CLASSBINDIR)/%.class)
|
FILES_class = $(FILES_java:%.java=$(CLASSBINDIR)/%.class)
|
||||||
@ -77,7 +79,7 @@ include $(BUILDDIR)/common/Rules.gmk
|
|||||||
ifdef OPENJDK
|
ifdef OPENJDK
|
||||||
build: properties policy cacerts
|
build: properties policy cacerts
|
||||||
else
|
else
|
||||||
build: properties policy cacerts blacklist
|
build: properties policy cacerts blacklist trustedlibs
|
||||||
endif
|
endif
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
@ -90,6 +92,8 @@ cacerts: classes $(CACERTS_BUILD)
|
|||||||
|
|
||||||
blacklist: classes $(BLACKLIST_BUILD)
|
blacklist: classes $(BLACKLIST_BUILD)
|
||||||
|
|
||||||
|
trustedlibs: classes $(TRUSTEDLIBS_BUILD)
|
||||||
|
|
||||||
$(PROPS_BUILD): $(PROPS_SRC)
|
$(PROPS_BUILD): $(PROPS_SRC)
|
||||||
$(install-file)
|
$(install-file)
|
||||||
|
|
||||||
@ -102,9 +106,12 @@ $(CACERTS_BUILD): $(CACERTS_SRC)
|
|||||||
$(BLACKLIST_BUILD): $(BLACKLIST_SRC)
|
$(BLACKLIST_BUILD): $(BLACKLIST_SRC)
|
||||||
$(install-file)
|
$(install-file)
|
||||||
|
|
||||||
|
$(TRUSTEDLIBS_BUILD): $(TRUSTEDLIBS_SRC)
|
||||||
|
$(install-file)
|
||||||
|
|
||||||
clean clobber:: .delete.classlist
|
clean clobber:: .delete.classlist
|
||||||
$(RM) -r $(CLASSBINDIR)/java/security
|
$(RM) -r $(CLASSBINDIR)/java/security
|
||||||
$(RM) $(PROPS_BUILD) $(POLICY_BUILD) $(CACERTS_BUILD) $(BLACKLIST_BUILD)
|
$(RM) $(PROPS_BUILD) $(POLICY_BUILD) $(CACERTS_BUILD) $(BLACKLIST_BUILD) $(TRUSTEDLIBS_BUILD)
|
||||||
|
|
||||||
# Additional Rule for building sun.security.util
|
# Additional Rule for building sun.security.util
|
||||||
$(CLASSBINDIR)/%.class: $(SHARE_SRC)/sun/%.java
|
$(CLASSBINDIR)/%.class: $(SHARE_SRC)/sun/%.java
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2011, 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
|
||||||
@ -27,11 +27,13 @@ package java.util.jar;
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.zip.*;
|
import java.util.zip.*;
|
||||||
import java.security.CodeSigner;
|
import java.security.CodeSigner;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
|
import java.security.CodeSource;
|
||||||
import sun.security.action.GetPropertyAction;
|
import sun.security.action.GetPropertyAction;
|
||||||
import sun.security.util.ManifestEntryVerifier;
|
import sun.security.util.ManifestEntryVerifier;
|
||||||
import sun.misc.SharedSecrets;
|
import sun.misc.SharedSecrets;
|
||||||
@ -262,7 +264,7 @@ class JarFile extends ZipFile {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
if (certs == null && jv != null) {
|
if (certs == null && jv != null) {
|
||||||
certs = jv.getCerts(getName());
|
certs = jv.getCerts(JarFile.this, this);
|
||||||
}
|
}
|
||||||
return certs == null ? null : certs.clone();
|
return certs == null ? null : certs.clone();
|
||||||
}
|
}
|
||||||
@ -273,7 +275,7 @@ class JarFile extends ZipFile {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
if (signers == null && jv != null) {
|
if (signers == null && jv != null) {
|
||||||
signers = jv.getCodeSigners(getName());
|
signers = jv.getCodeSigners(JarFile.this, this);
|
||||||
}
|
}
|
||||||
return signers == null ? null : signers.clone();
|
return signers == null ? null : signers.clone();
|
||||||
}
|
}
|
||||||
@ -544,4 +546,191 @@ class JarFile extends ZipFile {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized void ensureInitialization() {
|
||||||
|
try {
|
||||||
|
maybeInstantiateVerifier();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if (jv != null && !jvInitialized) {
|
||||||
|
initializeVerifier();
|
||||||
|
jvInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JarEntry newEntry(ZipEntry ze) {
|
||||||
|
return new JarFileEntry(ze);
|
||||||
|
}
|
||||||
|
|
||||||
|
Enumeration<String> entryNames(CodeSource[] cs) {
|
||||||
|
ensureInitialization();
|
||||||
|
if (jv != null) {
|
||||||
|
return jv.entryNames(this, cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* JAR file has no signed content. Is there a non-signing
|
||||||
|
* code source?
|
||||||
|
*/
|
||||||
|
boolean includeUnsigned = false;
|
||||||
|
for (int i = 0; i < cs.length; i++) {
|
||||||
|
if (cs[i].getCodeSigners() == null) {
|
||||||
|
includeUnsigned = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (includeUnsigned) {
|
||||||
|
return unsignedEntryNames();
|
||||||
|
} else {
|
||||||
|
return new Enumeration<String>() {
|
||||||
|
|
||||||
|
public boolean hasMoreElements() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String nextElement() {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an enumeration of the zip file entries
|
||||||
|
* excluding internal JAR mechanism entries and including
|
||||||
|
* signed entries missing from the ZIP directory.
|
||||||
|
*/
|
||||||
|
Enumeration<JarEntry> entries2() {
|
||||||
|
ensureInitialization();
|
||||||
|
if (jv != null) {
|
||||||
|
return jv.entries2(this, super.entries());
|
||||||
|
}
|
||||||
|
|
||||||
|
// screen out entries which are never signed
|
||||||
|
final Enumeration enum_ = super.entries();
|
||||||
|
return new Enumeration<JarEntry>() {
|
||||||
|
|
||||||
|
ZipEntry entry;
|
||||||
|
|
||||||
|
public boolean hasMoreElements() {
|
||||||
|
if (entry != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
while (enum_.hasMoreElements()) {
|
||||||
|
ZipEntry ze = (ZipEntry) enum_.nextElement();
|
||||||
|
if (JarVerifier.isSigningRelated(ze.getName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry = ze;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JarFileEntry nextElement() {
|
||||||
|
if (hasMoreElements()) {
|
||||||
|
ZipEntry ze = entry;
|
||||||
|
entry = null;
|
||||||
|
return new JarFileEntry(ze);
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeSource[] getCodeSources(URL url) {
|
||||||
|
ensureInitialization();
|
||||||
|
if (jv != null) {
|
||||||
|
return jv.getCodeSources(this, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* JAR file has no signed content. Is there a non-signing
|
||||||
|
* code source?
|
||||||
|
*/
|
||||||
|
Enumeration unsigned = unsignedEntryNames();
|
||||||
|
if (unsigned.hasMoreElements()) {
|
||||||
|
return new CodeSource[]{JarVerifier.getUnsignedCS(url)};
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Enumeration<String> unsignedEntryNames() {
|
||||||
|
final Enumeration entries = entries();
|
||||||
|
return new Enumeration<String>() {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grab entries from ZIP directory but screen out
|
||||||
|
* metadata.
|
||||||
|
*/
|
||||||
|
public boolean hasMoreElements() {
|
||||||
|
if (name != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
String value;
|
||||||
|
ZipEntry e = (ZipEntry) entries.nextElement();
|
||||||
|
value = e.getName();
|
||||||
|
if (e.isDirectory() || JarVerifier.isSigningRelated(value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
name = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String nextElement() {
|
||||||
|
if (hasMoreElements()) {
|
||||||
|
String value = name;
|
||||||
|
name = null;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeSource getCodeSource(URL url, String name) {
|
||||||
|
ensureInitialization();
|
||||||
|
if (jv != null) {
|
||||||
|
if (jv.eagerValidation) {
|
||||||
|
CodeSource cs = null;
|
||||||
|
JarEntry je = getJarEntry(name);
|
||||||
|
if (je != null) {
|
||||||
|
cs = jv.getCodeSource(url, this, je);
|
||||||
|
} else {
|
||||||
|
cs = jv.getCodeSource(url, name);
|
||||||
|
}
|
||||||
|
return cs;
|
||||||
|
} else {
|
||||||
|
return jv.getCodeSource(url, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JarVerifier.getUnsignedCS(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEagerValidation(boolean eager) {
|
||||||
|
try {
|
||||||
|
maybeInstantiateVerifier();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if (jv != null) {
|
||||||
|
jv.setEagerValidation(eager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List getManifestDigests() {
|
||||||
|
ensureInitialization();
|
||||||
|
if (jv != null) {
|
||||||
|
return jv.getManifestDigests();
|
||||||
|
}
|
||||||
|
return new ArrayList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2011, 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
|
||||||
@ -26,9 +26,11 @@
|
|||||||
package java.util.jar;
|
package java.util.jar;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
import sun.security.util.ManifestDigester;
|
import sun.security.util.ManifestDigester;
|
||||||
import sun.security.util.ManifestEntryVerifier;
|
import sun.security.util.ManifestEntryVerifier;
|
||||||
@ -81,6 +83,15 @@ class JarVerifier {
|
|||||||
/** the bytes for the manDig object */
|
/** the bytes for the manDig object */
|
||||||
byte manifestRawBytes[] = null;
|
byte manifestRawBytes[] = null;
|
||||||
|
|
||||||
|
/** controls eager signature validation */
|
||||||
|
boolean eagerValidation;
|
||||||
|
|
||||||
|
/** makes code source singleton instances unique to us */
|
||||||
|
private Object csdomain = new Object();
|
||||||
|
|
||||||
|
/** collect -DIGEST-MANIFEST values for blacklist */
|
||||||
|
private List manifestDigests;
|
||||||
|
|
||||||
public JarVerifier(byte rawBytes[]) {
|
public JarVerifier(byte rawBytes[]) {
|
||||||
manifestRawBytes = rawBytes;
|
manifestRawBytes = rawBytes;
|
||||||
sigFileSigners = new Hashtable();
|
sigFileSigners = new Hashtable();
|
||||||
@ -88,6 +99,7 @@ class JarVerifier {
|
|||||||
sigFileData = new Hashtable(11);
|
sigFileData = new Hashtable(11);
|
||||||
pendingBlocks = new ArrayList();
|
pendingBlocks = new ArrayList();
|
||||||
baos = new ByteArrayOutputStream();
|
baos = new ByteArrayOutputStream();
|
||||||
|
manifestDigests = new ArrayList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -247,7 +259,7 @@ class JarVerifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sfv.setSignatureFile(bytes);
|
sfv.setSignatureFile(bytes);
|
||||||
sfv.process(sigFileSigners);
|
sfv.process(sigFileSigners, manifestDigests);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -290,7 +302,7 @@ class JarVerifier {
|
|||||||
sfv.setSignatureFile(bytes);
|
sfv.setSignatureFile(bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sfv.process(sigFileSigners);
|
sfv.process(sigFileSigners, manifestDigests);
|
||||||
|
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
// e.g. sun.security.pkcs.ParsingException
|
// e.g. sun.security.pkcs.ParsingException
|
||||||
@ -312,12 +324,18 @@ class JarVerifier {
|
|||||||
/**
|
/**
|
||||||
* Return an array of java.security.cert.Certificate objects for
|
* Return an array of java.security.cert.Certificate objects for
|
||||||
* the given file in the jar.
|
* the given file in the jar.
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public java.security.cert.Certificate[] getCerts(String name)
|
public java.security.cert.Certificate[] getCerts(String name)
|
||||||
{
|
{
|
||||||
return mapSignersToCertArray(getCodeSigners(name));
|
return mapSignersToCertArray(getCodeSigners(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public java.security.cert.Certificate[] getCerts(JarFile jar, JarEntry entry)
|
||||||
|
{
|
||||||
|
return mapSignersToCertArray(getCodeSigners(jar, entry));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return an array of CodeSigner objects for
|
* return an array of CodeSigner objects for
|
||||||
* the given file in the jar. this array is not cloned.
|
* the given file in the jar. this array is not cloned.
|
||||||
@ -328,6 +346,28 @@ class JarVerifier {
|
|||||||
return (CodeSigner[])verifiedSigners.get(name);
|
return (CodeSigner[])verifiedSigners.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CodeSigner[] getCodeSigners(JarFile jar, JarEntry entry)
|
||||||
|
{
|
||||||
|
String name = entry.getName();
|
||||||
|
if (eagerValidation && sigFileSigners.get(name) != null) {
|
||||||
|
/*
|
||||||
|
* Force a read of the entry data to generate the
|
||||||
|
* verification hash.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
InputStream s = jar.getInputStream(entry);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int n = buffer.length;
|
||||||
|
while (n != -1) {
|
||||||
|
n = s.read(buffer, 0, buffer.length);
|
||||||
|
}
|
||||||
|
s.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getCodeSigners(name);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert an array of signers into an array of concatenated certificate
|
* Convert an array of signers into an array of concatenated certificate
|
||||||
* arrays.
|
* arrays.
|
||||||
@ -444,4 +484,393 @@ class JarVerifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extended JavaUtilJarAccess CodeSource API Support
|
||||||
|
|
||||||
|
private Map urlToCodeSourceMap = new HashMap();
|
||||||
|
private Map signerToCodeSource = new HashMap();
|
||||||
|
private URL lastURL;
|
||||||
|
private Map lastURLMap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a unique mapping from codeSigner cache entries to CodeSource.
|
||||||
|
* In theory, multiple URLs origins could map to a single locally cached
|
||||||
|
* and shared JAR file although in practice there will be a single URL in use.
|
||||||
|
*/
|
||||||
|
private synchronized CodeSource mapSignersToCodeSource(URL url, CodeSigner[] signers) {
|
||||||
|
Map map;
|
||||||
|
if (url == lastURL) {
|
||||||
|
map = lastURLMap;
|
||||||
|
} else {
|
||||||
|
map = (Map) urlToCodeSourceMap.get(url);
|
||||||
|
if (map == null) {
|
||||||
|
map = new HashMap();
|
||||||
|
urlToCodeSourceMap.put(url, map);
|
||||||
|
}
|
||||||
|
lastURLMap = map;
|
||||||
|
lastURL = url;
|
||||||
|
}
|
||||||
|
CodeSource cs = (CodeSource) map.get(signers);
|
||||||
|
if (cs == null) {
|
||||||
|
cs = new VerifierCodeSource(csdomain, url, signers);
|
||||||
|
signerToCodeSource.put(signers, cs);
|
||||||
|
}
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CodeSource[] mapSignersToCodeSources(URL url, List signers, boolean unsigned) {
|
||||||
|
List sources = new ArrayList();
|
||||||
|
|
||||||
|
for (int i = 0; i < signers.size(); i++) {
|
||||||
|
sources.add(mapSignersToCodeSource(url, (CodeSigner[]) signers.get(i)));
|
||||||
|
}
|
||||||
|
if (unsigned) {
|
||||||
|
sources.add(mapSignersToCodeSource(url, null));
|
||||||
|
}
|
||||||
|
return (CodeSource[]) sources.toArray(new CodeSource[sources.size()]);
|
||||||
|
}
|
||||||
|
private CodeSigner[] emptySigner = new CodeSigner[0];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Match CodeSource to a CodeSigner[] in the signer cache.
|
||||||
|
*/
|
||||||
|
private CodeSigner[] findMatchingSigners(CodeSource cs) {
|
||||||
|
if (cs instanceof VerifierCodeSource) {
|
||||||
|
VerifierCodeSource vcs = (VerifierCodeSource) cs;
|
||||||
|
if (vcs.isSameDomain(csdomain)) {
|
||||||
|
return ((VerifierCodeSource) cs).getPrivateSigners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In practice signers should always be optimized above
|
||||||
|
* but this handles a CodeSource of any type, just in case.
|
||||||
|
*/
|
||||||
|
CodeSource[] sources = mapSignersToCodeSources(cs.getLocation(), getJarCodeSigners(), true);
|
||||||
|
List sourceList = new ArrayList();
|
||||||
|
for (int i = 0; i < sources.length; i++) {
|
||||||
|
sourceList.add(sources[i]);
|
||||||
|
}
|
||||||
|
int j = sourceList.indexOf(cs);
|
||||||
|
if (j != -1) {
|
||||||
|
CodeSigner[] match;
|
||||||
|
match = ((VerifierCodeSource) sourceList.get(j)).getPrivateSigners();
|
||||||
|
if (match == null) {
|
||||||
|
match = emptySigner;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Instances of this class hold uncopied references to internal
|
||||||
|
* signing data that can be compared by object reference identity.
|
||||||
|
*/
|
||||||
|
private static class VerifierCodeSource extends CodeSource {
|
||||||
|
|
||||||
|
URL vlocation;
|
||||||
|
CodeSigner[] vsigners;
|
||||||
|
java.security.cert.Certificate[] vcerts;
|
||||||
|
Object csdomain;
|
||||||
|
|
||||||
|
VerifierCodeSource(Object csdomain, URL location, CodeSigner[] signers) {
|
||||||
|
super(location, signers);
|
||||||
|
this.csdomain = csdomain;
|
||||||
|
vlocation = location;
|
||||||
|
vsigners = signers; // from signerCache
|
||||||
|
}
|
||||||
|
|
||||||
|
VerifierCodeSource(Object csdomain, URL location, java.security.cert.Certificate[] certs) {
|
||||||
|
super(location, certs);
|
||||||
|
this.csdomain = csdomain;
|
||||||
|
vlocation = location;
|
||||||
|
vcerts = certs; // from signerCache
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All VerifierCodeSource instances are constructed based on
|
||||||
|
* singleton signerCache or signerCacheCert entries for each unique signer.
|
||||||
|
* No CodeSigner<->Certificate[] conversion is required.
|
||||||
|
* We use these assumptions to optimize equality comparisons.
|
||||||
|
*/
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof VerifierCodeSource) {
|
||||||
|
VerifierCodeSource that = (VerifierCodeSource) obj;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only compare against other per-signer singletons constructed
|
||||||
|
* on behalf of the same JarFile instance. Otherwise, compare
|
||||||
|
* things the slower way.
|
||||||
|
*/
|
||||||
|
if (isSameDomain(that.csdomain)) {
|
||||||
|
if (that.vsigners != this.vsigners
|
||||||
|
|| that.vcerts != this.vcerts) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (that.vlocation != null) {
|
||||||
|
return that.vlocation.equals(this.vlocation);
|
||||||
|
} else if (this.vlocation != null) {
|
||||||
|
return this.vlocation.equals(that.vlocation);
|
||||||
|
} else { // both null
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.equals(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isSameDomain(Object csdomain) {
|
||||||
|
return this.csdomain == csdomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CodeSigner[] getPrivateSigners() {
|
||||||
|
return vsigners;
|
||||||
|
}
|
||||||
|
|
||||||
|
private java.security.cert.Certificate[] getPrivateCertificates() {
|
||||||
|
return vcerts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private Map signerMap;
|
||||||
|
|
||||||
|
private synchronized Map signerMap() {
|
||||||
|
if (signerMap == null) {
|
||||||
|
/*
|
||||||
|
* Snapshot signer state so it doesn't change on us. We care
|
||||||
|
* only about the asserted signatures. Verification of
|
||||||
|
* signature validity happens via the JarEntry apis.
|
||||||
|
*/
|
||||||
|
signerMap = new HashMap(verifiedSigners.size() + sigFileSigners.size());
|
||||||
|
signerMap.putAll(verifiedSigners);
|
||||||
|
signerMap.putAll(sigFileSigners);
|
||||||
|
}
|
||||||
|
return signerMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized Enumeration<String> entryNames(JarFile jar, final CodeSource[] cs) {
|
||||||
|
final Map map = signerMap();
|
||||||
|
final Iterator itor = map.entrySet().iterator();
|
||||||
|
boolean matchUnsigned = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grab a single copy of the CodeSigner arrays. Check
|
||||||
|
* to see if we can optimize CodeSigner equality test.
|
||||||
|
*/
|
||||||
|
List req = new ArrayList(cs.length);
|
||||||
|
for (int i = 0; i < cs.length; i++) {
|
||||||
|
CodeSigner[] match = findMatchingSigners(cs[i]);
|
||||||
|
if (match != null) {
|
||||||
|
if (match.length > 0) {
|
||||||
|
req.add(match);
|
||||||
|
} else {
|
||||||
|
matchUnsigned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final List signersReq = req;
|
||||||
|
final Enumeration enum2 = (matchUnsigned) ? unsignedEntryNames(jar) : emptyEnumeration;
|
||||||
|
|
||||||
|
return new Enumeration<String>() {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
|
||||||
|
public boolean hasMoreElements() {
|
||||||
|
if (name != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (itor.hasNext()) {
|
||||||
|
Map.Entry e = (Map.Entry) itor.next();
|
||||||
|
if (signersReq.contains((CodeSigner[]) e.getValue())) {
|
||||||
|
name = (String) e.getKey();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (enum2.hasMoreElements()) {
|
||||||
|
name = (String) enum2.nextElement();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String nextElement() {
|
||||||
|
if (hasMoreElements()) {
|
||||||
|
String value = name;
|
||||||
|
name = null;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Like entries() but screens out internal JAR mechanism entries
|
||||||
|
* and includes signed entries with no ZIP data.
|
||||||
|
*/
|
||||||
|
public Enumeration<JarEntry> entries2(final JarFile jar, Enumeration e) {
|
||||||
|
final Map map = new HashMap();
|
||||||
|
map.putAll(signerMap());
|
||||||
|
final Enumeration enum_ = e;
|
||||||
|
return new Enumeration<JarEntry>() {
|
||||||
|
|
||||||
|
Enumeration signers = null;
|
||||||
|
JarEntry entry;
|
||||||
|
|
||||||
|
public boolean hasMoreElements() {
|
||||||
|
if (entry != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
while (enum_.hasMoreElements()) {
|
||||||
|
ZipEntry ze = (ZipEntry) enum_.nextElement();
|
||||||
|
if (JarVerifier.isSigningRelated(ze.getName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry = jar.newEntry(ze);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (signers == null) {
|
||||||
|
signers = Collections.enumeration(map.keySet());
|
||||||
|
}
|
||||||
|
while (signers.hasMoreElements()) {
|
||||||
|
String name = (String) signers.nextElement();
|
||||||
|
entry = jar.newEntry(new ZipEntry(name));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any map entries left?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JarEntry nextElement() {
|
||||||
|
if (hasMoreElements()) {
|
||||||
|
JarEntry je = entry;
|
||||||
|
map.remove(je.getName());
|
||||||
|
entry = null;
|
||||||
|
return je;
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private Enumeration emptyEnumeration = new Enumeration<String>() {
|
||||||
|
|
||||||
|
public boolean hasMoreElements() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String nextElement() {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// true if file is part of the signature mechanism itself
|
||||||
|
static boolean isSigningRelated(String name) {
|
||||||
|
name = name.toUpperCase(Locale.ENGLISH);
|
||||||
|
if (!name.startsWith("META-INF/")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
name = name.substring(9);
|
||||||
|
if (name.indexOf('/') != -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (name.endsWith(".DSA")
|
||||||
|
|| name.endsWith(".RSA")
|
||||||
|
|| name.endsWith(".SF")
|
||||||
|
|| name.endsWith(".EC")
|
||||||
|
|| name.startsWith("SIG-")
|
||||||
|
|| name.equals("MANIFEST.MF")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Enumeration<String> unsignedEntryNames(JarFile jar) {
|
||||||
|
final Map map = signerMap();
|
||||||
|
final Enumeration entries = jar.entries();
|
||||||
|
return new Enumeration<String>() {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grab entries from ZIP directory but screen out
|
||||||
|
* metadata.
|
||||||
|
*/
|
||||||
|
public boolean hasMoreElements() {
|
||||||
|
if (name != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
String value;
|
||||||
|
ZipEntry e = (ZipEntry) entries.nextElement();
|
||||||
|
value = e.getName();
|
||||||
|
if (e.isDirectory() || isSigningRelated(value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (map.get(value) == null) {
|
||||||
|
name = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String nextElement() {
|
||||||
|
if (hasMoreElements()) {
|
||||||
|
String value = name;
|
||||||
|
name = null;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private List jarCodeSigners;
|
||||||
|
|
||||||
|
private synchronized List getJarCodeSigners() {
|
||||||
|
CodeSigner[] signers;
|
||||||
|
if (jarCodeSigners == null) {
|
||||||
|
HashSet set = new HashSet();
|
||||||
|
set.addAll(signerMap().values());
|
||||||
|
jarCodeSigners = new ArrayList();
|
||||||
|
jarCodeSigners.addAll(set);
|
||||||
|
}
|
||||||
|
return jarCodeSigners;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized CodeSource[] getCodeSources(JarFile jar, URL url) {
|
||||||
|
boolean hasUnsigned = unsignedEntryNames(jar).hasMoreElements();
|
||||||
|
|
||||||
|
return mapSignersToCodeSources(url, getJarCodeSigners(), hasUnsigned);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeSource getCodeSource(URL url, String name) {
|
||||||
|
CodeSigner[] signers;
|
||||||
|
|
||||||
|
signers = (CodeSigner[]) signerMap().get(name);
|
||||||
|
return mapSignersToCodeSource(url, signers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeSource getCodeSource(URL url, JarFile jar, JarEntry je) {
|
||||||
|
CodeSigner[] signers;
|
||||||
|
|
||||||
|
return mapSignersToCodeSource(url, getCodeSigners(jar, je));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEagerValidation(boolean eager) {
|
||||||
|
eagerValidation = eager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized List getManifestDigests() {
|
||||||
|
return Collections.unmodifiableList(manifestDigests);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CodeSource getUnsignedCS(URL url) {
|
||||||
|
return new VerifierCodeSource(null, url, (java.security.cert.Certificate[]) null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2011, 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
|
||||||
@ -26,10 +26,38 @@
|
|||||||
package java.util.jar;
|
package java.util.jar;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.security.CodeSource;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
import sun.misc.JavaUtilJarAccess;
|
import sun.misc.JavaUtilJarAccess;
|
||||||
|
|
||||||
class JavaUtilJarAccessImpl implements JavaUtilJarAccess {
|
class JavaUtilJarAccessImpl implements JavaUtilJarAccess {
|
||||||
public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException {
|
public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException {
|
||||||
return jar.hasClassPathAttribute();
|
return jar.hasClassPathAttribute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CodeSource[] getCodeSources(JarFile jar, URL url) {
|
||||||
|
return jar.getCodeSources(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeSource getCodeSource(JarFile jar, URL url, String name) {
|
||||||
|
return jar.getCodeSource(url, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration<String> entryNames(JarFile jar, CodeSource[] cs) {
|
||||||
|
return jar.entryNames(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration<JarEntry> entries2(JarFile jar) {
|
||||||
|
return jar.entries2();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEagerValidation(JarFile jar, boolean eager) {
|
||||||
|
jar.setEagerValidation(eager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List getManifestDigests(JarFile jar) {
|
||||||
|
return jar.getManifestDigests();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,19 @@ public class JarIndex {
|
|||||||
parseJars(files);
|
parseJars(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the jar index, or <code>null</code> if none.
|
||||||
|
*
|
||||||
|
* This single parameter version of the method is retained
|
||||||
|
* for binary compatibility with earlier releases.
|
||||||
|
*
|
||||||
|
* @param jar the JAR file to get the index from.
|
||||||
|
* @exception IOException if an I/O error has occurred.
|
||||||
|
*/
|
||||||
|
public static JarIndex getJarIndex(JarFile jar) throws IOException {
|
||||||
|
return getJarIndex(jar, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the jar index, or <code>null</code> if none.
|
* Returns the jar index, or <code>null</code> if none.
|
||||||
*
|
*
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2002, 2011, 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
|
||||||
@ -26,8 +26,19 @@
|
|||||||
package sun.misc;
|
package sun.misc;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.security.CodeSource;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
public interface JavaUtilJarAccess {
|
public interface JavaUtilJarAccess {
|
||||||
public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException;
|
public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException;
|
||||||
|
public CodeSource[] getCodeSources(JarFile jar, URL url);
|
||||||
|
public CodeSource getCodeSource(JarFile jar, URL url, String name);
|
||||||
|
public Enumeration<String> entryNames(JarFile jar, CodeSource[] cs);
|
||||||
|
public Enumeration<JarEntry> entries2(JarFile jar);
|
||||||
|
public void setEagerValidation(JarFile jar, boolean eager);
|
||||||
|
public List getManifestDigests(JarFile jar);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1997, 2011, 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
|
||||||
@ -181,7 +181,8 @@ public class SignatureFileVerifier {
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void process(Hashtable<String, CodeSigner[]> signers)
|
public void process(Hashtable<String, CodeSigner[]> signers,
|
||||||
|
List manifestDigests)
|
||||||
throws IOException, SignatureException, NoSuchAlgorithmException,
|
throws IOException, SignatureException, NoSuchAlgorithmException,
|
||||||
JarException, CertificateException
|
JarException, CertificateException
|
||||||
{
|
{
|
||||||
@ -190,14 +191,15 @@ public class SignatureFileVerifier {
|
|||||||
Object obj = null;
|
Object obj = null;
|
||||||
try {
|
try {
|
||||||
obj = Providers.startJarVerification();
|
obj = Providers.startJarVerification();
|
||||||
processImpl(signers);
|
processImpl(signers, manifestDigests);
|
||||||
} finally {
|
} finally {
|
||||||
Providers.stopJarVerification(obj);
|
Providers.stopJarVerification(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processImpl(Hashtable<String, CodeSigner[]> signers)
|
private void processImpl(Hashtable<String, CodeSigner[]> signers,
|
||||||
|
List manifestDigests)
|
||||||
throws IOException, SignatureException, NoSuchAlgorithmException,
|
throws IOException, SignatureException, NoSuchAlgorithmException,
|
||||||
JarException, CertificateException
|
JarException, CertificateException
|
||||||
{
|
{
|
||||||
@ -232,7 +234,7 @@ public class SignatureFileVerifier {
|
|||||||
sf.getEntries().entrySet().iterator();
|
sf.getEntries().entrySet().iterator();
|
||||||
|
|
||||||
// see if we can verify the whole manifest first
|
// see if we can verify the whole manifest first
|
||||||
boolean manifestSigned = verifyManifestHash(sf, md, decoder);
|
boolean manifestSigned = verifyManifestHash(sf, md, decoder, manifestDigests);
|
||||||
|
|
||||||
// verify manifest main attributes
|
// verify manifest main attributes
|
||||||
if (!manifestSigned && !verifyManifestMainAttrs(sf, md, decoder)) {
|
if (!manifestSigned && !verifyManifestMainAttrs(sf, md, decoder)) {
|
||||||
@ -275,7 +277,8 @@ public class SignatureFileVerifier {
|
|||||||
*/
|
*/
|
||||||
private boolean verifyManifestHash(Manifest sf,
|
private boolean verifyManifestHash(Manifest sf,
|
||||||
ManifestDigester md,
|
ManifestDigester md,
|
||||||
BASE64Decoder decoder)
|
BASE64Decoder decoder,
|
||||||
|
List manifestDigests)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
Attributes mattr = sf.getMainAttributes();
|
Attributes mattr = sf.getMainAttributes();
|
||||||
@ -290,6 +293,8 @@ public class SignatureFileVerifier {
|
|||||||
// 16 is length of "-Digest-Manifest"
|
// 16 is length of "-Digest-Manifest"
|
||||||
String algorithm = key.substring(0, key.length()-16);
|
String algorithm = key.substring(0, key.length()-16);
|
||||||
|
|
||||||
|
manifestDigests.add(key);
|
||||||
|
manifestDigests.add(se.getValue());
|
||||||
MessageDigest digest = getDigest(algorithm);
|
MessageDigest digest = getDigest(algorithm);
|
||||||
if (digest != null) {
|
if (digest != null) {
|
||||||
byte[] computedHash = md.manifestDigest(digest);
|
byte[] computedHash = md.manifestDigest(digest);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user