8215937: Check usages of security-related Resources files
Reviewed-by: mullan
This commit is contained in:
parent
db89805fe0
commit
54815061a1
@ -3784,9 +3784,9 @@ public final class Main {
|
||||
replyCerts.length);
|
||||
tmpCerts[tmpCerts.length-1] = root.snd;
|
||||
replyCerts = tmpCerts;
|
||||
checkWeak(String.format(rb.getString(fromKeyStore ?
|
||||
"alias.in.keystore" :
|
||||
"alias.in.cacerts"),
|
||||
checkWeak(String.format(fromKeyStore
|
||||
? rb.getString("alias.in.keystore")
|
||||
: rb.getString("alias.in.cacerts"),
|
||||
root.fst),
|
||||
root.snd);
|
||||
}
|
||||
|
@ -253,7 +253,6 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
{"Keystore.password.is.too.short.must.be.at.least.6.characters",
|
||||
"Keystore password is too short - must be at least 6 characters"},
|
||||
{"Unknown.Entry.Type", "Unknown Entry Type"},
|
||||
{"Too.many.failures.Alias.not.changed", "Too many failures. Alias not changed"},
|
||||
{"Entry.for.alias.alias.successfully.imported.",
|
||||
"Entry for alias {0} successfully imported."},
|
||||
{"Entry.for.alias.alias.not.imported.", "Entry for alias {0} not imported."},
|
||||
@ -314,10 +313,6 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
{"Too.many.failures.Key.entry.not.cloned",
|
||||
"Too many failures. Key entry not cloned"},
|
||||
{"key.password.for.alias.", "key password for <{0}>"},
|
||||
{"Keystore.entry.for.id.getName.already.exists",
|
||||
"Keystore entry for <{0}> already exists"},
|
||||
{"Creating.keystore.entry.for.id.getName.",
|
||||
"Creating keystore entry for <{0}> ..."},
|
||||
{"No.entries.from.identity.database.added",
|
||||
"No entries from identity database added"},
|
||||
{"Alias.name.alias", "Alias name: {0}"},
|
||||
@ -355,7 +350,6 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
{"Do.you.still.want.to.add.it.to.your.own.keystore.no.",
|
||||
"Do you still want to add it to your own keystore? [no]: "},
|
||||
{"Trust.this.certificate.no.", "Trust this certificate? [no]: "},
|
||||
{"YES", "YES"},
|
||||
{"New.prompt.", "New {0}: "},
|
||||
{"Passwords.must.differ", "Passwords must differ"},
|
||||
{"Re.enter.new.prompt.", "Re-enter new {0}: "},
|
||||
@ -395,7 +389,6 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
{"Signer.d.", "Signer #%d:"},
|
||||
{"Timestamp.", "Timestamp:"},
|
||||
{"Signature.", "Signature:"},
|
||||
{"CRLs.", "CRLs:"},
|
||||
{"Certificate.owner.", "Certificate owner: "},
|
||||
{"Not.a.signed.jar.file", "Not a signed jar file"},
|
||||
{"No.certificate.from.the.SSL.server",
|
||||
@ -414,13 +407,10 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
"Certificate reply does not contain public key for <{0}>"},
|
||||
{"Incomplete.certificate.chain.in.reply",
|
||||
"Incomplete certificate chain in reply"},
|
||||
{"Certificate.chain.in.reply.does.not.verify.",
|
||||
"Certificate chain in reply does not verify: "},
|
||||
{"Top.level.certificate.in.reply.",
|
||||
"Top-level certificate in reply:\n"},
|
||||
{".is.not.trusted.", "... is not trusted. "},
|
||||
{"Install.reply.anyway.no.", "Install reply anyway? [no]: "},
|
||||
{"NO", "NO"},
|
||||
{"Public.keys.in.reply.and.keystore.don.t.match",
|
||||
"Public keys in reply and keystore don't match"},
|
||||
{"Certificate.reply.and.certificate.in.keystore.are.identical",
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 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
|
||||
@ -66,8 +66,6 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
{"Subject.", "Subject:\n"},
|
||||
{".Principal.", "\tPrincipal: "},
|
||||
{".Public.Credential.", "\tPublic Credential: "},
|
||||
{".Private.Credentials.inaccessible.",
|
||||
"\tPrivate Credentials inaccessible\n"},
|
||||
{".Private.Credential.", "\tPrivate Credential: "},
|
||||
{".Private.Credential.inaccessible.",
|
||||
"\tPrivate Credential inaccessible\n"},
|
||||
@ -89,16 +87,6 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
"invalid null CallbackHandler provided"},
|
||||
{"null.subject.logout.called.before.login",
|
||||
"null subject - logout called before login"},
|
||||
{"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor",
|
||||
"unable to instantiate LoginModule, {0}, because it does not provide a no-argument constructor"},
|
||||
{"unable.to.instantiate.LoginModule",
|
||||
"unable to instantiate LoginModule"},
|
||||
{"unable.to.instantiate.LoginModule.",
|
||||
"unable to instantiate LoginModule: "},
|
||||
{"unable.to.find.LoginModule.class.",
|
||||
"unable to find LoginModule class: "},
|
||||
{"unable.to.access.LoginModule.",
|
||||
"unable to access LoginModule: "},
|
||||
{"Login.Failure.all.modules.ignored",
|
||||
"Login Failure: all modules ignored"},
|
||||
|
||||
|
@ -1030,31 +1030,31 @@ public class Main {
|
||||
(hasExpiredTsaCert && !signerNotExpired)) {
|
||||
|
||||
if (strict) {
|
||||
result = rb.getString(isSigning
|
||||
? "jar.signed.with.signer.errors."
|
||||
: "jar.verified.with.signer.errors.");
|
||||
result = isSigning
|
||||
? rb.getString("jar.signed.with.signer.errors.")
|
||||
: rb.getString("jar.verified.with.signer.errors.");
|
||||
} else {
|
||||
result = rb.getString(isSigning
|
||||
? "jar.signed."
|
||||
: "jar.verified.");
|
||||
result = isSigning
|
||||
? rb.getString("jar.signed.")
|
||||
: rb.getString("jar.verified.");
|
||||
}
|
||||
|
||||
if (badKeyUsage) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
|
||||
errors.add(isSigning
|
||||
? rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")
|
||||
: rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
if (badExtendedKeyUsage) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
|
||||
errors.add(isSigning
|
||||
? rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")
|
||||
: rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
if (badNetscapeCertType) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
|
||||
errors.add(isSigning
|
||||
? rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")
|
||||
: rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
// only in verifying
|
||||
@ -1063,20 +1063,20 @@ public class Main {
|
||||
"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked."));
|
||||
}
|
||||
if (hasExpiredCert) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.has.expired."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.has.expired."));
|
||||
errors.add(isSigning
|
||||
? rb.getString("The.signer.certificate.has.expired.")
|
||||
: rb.getString("This.jar.contains.entries.whose.signer.certificate.has.expired."));
|
||||
}
|
||||
if (notYetValidCert) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.is.not.yet.valid."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));
|
||||
errors.add(isSigning
|
||||
? rb.getString("The.signer.certificate.is.not.yet.valid.")
|
||||
: rb.getString("This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));
|
||||
}
|
||||
|
||||
if (chainNotValidated) {
|
||||
errors.add(String.format(rb.getString(isSigning
|
||||
? "The.signer.s.certificate.chain.is.invalid.reason.1"
|
||||
: "This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"),
|
||||
errors.add(String.format(isSigning
|
||||
? rb.getString("The.signer.s.certificate.chain.is.invalid.reason.1")
|
||||
: rb.getString("This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"),
|
||||
chainNotValidatedReason.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
@ -1084,9 +1084,9 @@ public class Main {
|
||||
errors.add(rb.getString("The.timestamp.has.expired."));
|
||||
}
|
||||
if (tsaChainNotValidated) {
|
||||
errors.add(String.format(rb.getString(isSigning
|
||||
? "The.tsa.certificate.chain.is.invalid.reason.1"
|
||||
: "This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"),
|
||||
errors.add(String.format(isSigning
|
||||
? rb.getString("The.tsa.certificate.chain.is.invalid.reason.1")
|
||||
: rb.getString("This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"),
|
||||
tsaChainNotValidatedReason.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
@ -1102,9 +1102,9 @@ public class Main {
|
||||
}
|
||||
|
||||
if (signerSelfSigned) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.s.certificate.is.self.signed."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.is.self.signed."));
|
||||
errors.add(isSigning
|
||||
? rb.getString("The.signer.s.certificate.is.self.signed.")
|
||||
: rb.getString("This.jar.contains.entries.whose.signer.certificate.is.self.signed."));
|
||||
}
|
||||
|
||||
// weakAlg only detected in signing. The jar file is
|
||||
@ -1131,7 +1131,7 @@ public class Main {
|
||||
privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
|
||||
}
|
||||
} else {
|
||||
result = rb.getString(isSigning ? "jar.signed." : "jar.verified.");
|
||||
result = isSigning ? rb.getString("jar.signed.") : rb.getString("jar.verified.");
|
||||
}
|
||||
|
||||
if (hasExpiredTsaCert) {
|
||||
@ -1155,9 +1155,9 @@ public class Main {
|
||||
hasExpiredTsaCert = false;
|
||||
}
|
||||
if (hasExpiringCert) {
|
||||
warnings.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.will.expire.within.six.months."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
|
||||
warnings.add(isSigning
|
||||
? rb.getString("The.signer.certificate.will.expire.within.six.months.")
|
||||
: rb.getString("This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
|
||||
}
|
||||
if (hasExpiringTsaCert && expireDate != null) {
|
||||
if (expireDate.after(tsaExpireDate)) {
|
||||
@ -1170,13 +1170,13 @@ public class Main {
|
||||
}
|
||||
if (noTimestamp && expireDate != null) {
|
||||
if (hasTimestampBlock) {
|
||||
warnings.add(String.format(rb.getString(isSigning
|
||||
? "invalid.timestamp.signing"
|
||||
: "bad.timestamp.verifying"), expireDate));
|
||||
warnings.add(String.format(isSigning
|
||||
? rb.getString("invalid.timestamp.signing")
|
||||
: rb.getString("bad.timestamp.verifying"), expireDate));
|
||||
} else {
|
||||
warnings.add(String.format(rb.getString(isSigning
|
||||
? "no.timestamp.signing"
|
||||
: "no.timestamp.verifying"), expireDate));
|
||||
warnings.add(String.format(isSigning
|
||||
? rb.getString("no.timestamp.signing")
|
||||
: rb.getString("no.timestamp.verifying"), expireDate));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1551,7 +1551,20 @@ public class Main {
|
||||
|
||||
if (verbose != null) {
|
||||
builder.eventHandler((action, file) -> {
|
||||
System.out.println(rb.getString("." + action + ".") + file);
|
||||
switch (action) {
|
||||
case "signing":
|
||||
System.out.println(rb.getString(".signing.") + file);
|
||||
break;
|
||||
case "adding":
|
||||
System.out.println(rb.getString(".adding.") + file);
|
||||
break;
|
||||
case "updating":
|
||||
System.out.println(rb.getString(".updating.") + file);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown action: "
|
||||
+ action);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,6 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
|
||||
// shared (from jarsigner)
|
||||
{"SPACE", " "},
|
||||
{"2SPACE", " "},
|
||||
{"6SPACE", " "},
|
||||
{"COMMA", ", "},
|
||||
|
||||
@ -196,7 +195,6 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
"Certificate chain not found in the file specified."},
|
||||
{"found.non.X.509.certificate.in.signer.s.chain",
|
||||
"found non-X.509 certificate in signer's chain"},
|
||||
{"incomplete.certificate.chain", "incomplete certificate chain"},
|
||||
{"Enter.key.password.for.alias.", "Enter key password for {0}: "},
|
||||
{"unable.to.recover.key.from.keystore",
|
||||
"unable to recover key from keystore"},
|
||||
@ -240,8 +238,6 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
"This jar contains entries whose signer certificate is not yet valid. "},
|
||||
{"This.jar.contains.entries.whose.signer.certificate.is.self.signed.",
|
||||
"This jar contains entries whose signer certificate is self-signed."},
|
||||
{"Re.run.with.the.verbose.option.for.more.details.",
|
||||
"Re-run with the -verbose option for more details."},
|
||||
{"Re.run.with.the.verbose.and.certs.options.for.more.details.",
|
||||
"Re-run with the -verbose and -certs options for more details."},
|
||||
{"The.signer.certificate.has.expired.",
|
||||
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2012, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6987827
|
||||
* @modules java.base/sun.security.util
|
||||
* java.base/sun.security.tools.keytool
|
||||
* jdk.jartool/sun.security.tools.jarsigner
|
||||
* @summary security/util/Resources.java needs improvement
|
||||
*/
|
||||
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This test makes sure that the keys in resources files are using the new
|
||||
* format and there is no duplication.
|
||||
*/
|
||||
public class NewNamesFormat {
|
||||
public static void main(String[] args) throws Exception {
|
||||
checkRes("sun.security.util.Resources");
|
||||
checkRes("sun.security.util.AuthResources");
|
||||
checkRes("sun.security.tools.jarsigner.Resources");
|
||||
checkRes("sun.security.tools.keytool.Resources");
|
||||
}
|
||||
|
||||
private static void checkRes(String resName) throws Exception {
|
||||
System.out.println("Checking " + resName + "...");
|
||||
Class clazz = Class.forName(resName);
|
||||
Method m = clazz.getMethod("getContents");
|
||||
Object[][] contents = (Object[][])m.invoke(clazz.newInstance());
|
||||
Set<String> keys = new HashSet<String>();
|
||||
for (Object[] pair: contents) {
|
||||
String key = (String)pair[0];
|
||||
if (keys.contains(key)) {
|
||||
System.out.println("Found dup: " + key);
|
||||
throw new Exception();
|
||||
}
|
||||
checkKey(key);
|
||||
keys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkKey(String key) throws Exception {
|
||||
for (char c: key.toCharArray()) {
|
||||
if (Character.isLetter(c) || Character.isDigit(c) ||
|
||||
c == '{' || c == '}' || c == '.') {
|
||||
// OK
|
||||
} else {
|
||||
System.out.println("Illegal char [" + c + "] in key: " + key);
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,441 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2012, 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.
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.ListResourceBundle;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Prepares new key names for Resources.java.
|
||||
* 6987827: security/util/Resources.java needs improvement
|
||||
*
|
||||
* Run inside jdk/src/share/classes:
|
||||
*
|
||||
* java NewResourcesNames $(
|
||||
* for a in $(find com/sun/security sun/security javax/security -type f); do
|
||||
* egrep -q '(ResourcesMgr.getString|rb.getString)' $a && echo $a
|
||||
* done)
|
||||
*
|
||||
* Before running this tool, run the following two commands to make sure there
|
||||
* are only these 2 types of calls into the resources:
|
||||
* for a in `find com/sun/security sun/security javax/security -type f`; do
|
||||
* cat $a | perl -ne 'print if /\bResourcesMgr\b/'; done |
|
||||
* grep -v ResourcesMgr.getString
|
||||
* for a in `find com/sun/security sun/security -type f`; do
|
||||
* cat $a | perl -ne 'print if /\brb\b/'; done |
|
||||
* grep -v rb.getString
|
||||
*/
|
||||
class NewResourcesNames {
|
||||
|
||||
// Max length of normalized names
|
||||
static int MAXLEN = 127;
|
||||
|
||||
static String[] resources = {
|
||||
"sun/security/tools/jarsigner/Resources.java",
|
||||
"sun/security/tools/keytool/Resources.java",
|
||||
"sun/security/tools/policytool/Resources.java",
|
||||
"sun/security/util/Resources.java",
|
||||
"sun/security/util/AuthResources.java",
|
||||
};
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
// Load all names inside resources files
|
||||
Map<String,String> allnames = loadResources();
|
||||
|
||||
// Modify the callers. There are two patterns:
|
||||
// 1. ResourcesMgr.getString("
|
||||
// used by most JAAS codes
|
||||
// 2. rb.getString("
|
||||
// used by tools
|
||||
Set<String> allfound = new HashSet<String>();
|
||||
for (String arg: args) {
|
||||
allfound.addAll(rewriteFile(arg, "ResourcesMgr.getString(\""));
|
||||
allfound.addAll(rewriteFile(arg, "rb.getString(\""));
|
||||
}
|
||||
|
||||
// Special case 1: KeyTool's enum definition of commands and options
|
||||
allfound.addAll(keyToolEnums());
|
||||
|
||||
// Special case 2: PolicyFile called this 4 times
|
||||
allfound.addAll(rewriteFile("sun/security/provider/PolicyFile.java",
|
||||
"ResourcesMgr.getString(POLICY+\""));
|
||||
|
||||
// During the calls above, you can read sth like:
|
||||
//
|
||||
// Working on com/sun/security/auth/PolicyParser.java
|
||||
// GOOD match is 17
|
||||
//
|
||||
// This means a " exists right after getString(. Sometimes you see
|
||||
//
|
||||
// Working on sun/security/tools/keytool/Main.java
|
||||
// BAD!! pmatch != match: 212 != 209
|
||||
// Working on sun/security/provider/PolicyFile.java
|
||||
// BAD!! pmatch != match: 14 != 10
|
||||
//
|
||||
// which is mismatch. There are only two such special cases list above.
|
||||
// For KeyTool, there are 3 calls for showing help. For PolicyTool, 3
|
||||
// for name prefixed with POLICY. They are covered in the two special
|
||||
// cases above.
|
||||
|
||||
// Names used but not defined. This is mostly error, except for
|
||||
// special case 2 above. So it's OK to see 3 entries red here
|
||||
if (!allnames.keySet().containsAll(allfound)) {
|
||||
err("FATAL: Undefined names");
|
||||
for (String name: allfound) {
|
||||
if (!allnames.keySet().contains(name)) {
|
||||
err(" " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Names defined but not used. Mostly this is old entries not removed.
|
||||
// When soemone remove a line of code, he dares not remove the entry
|
||||
// in case it's also used somewhere else.
|
||||
if (!allfound.containsAll(allnames.keySet())) {
|
||||
System.err.println("WARNING: Unused names");
|
||||
for (String name: allnames.keySet()) {
|
||||
if (!allfound.contains(name)) {
|
||||
System.err.println(allnames.get(name));
|
||||
System.err.println(" " + normalize(name));
|
||||
System.err.println(" [" + name + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads the three resources files. Saves names into a Map.
|
||||
*/
|
||||
private static Map<String,String> loadResources() throws Exception {
|
||||
|
||||
// Name vs Resource
|
||||
Map<String,String> allnames = new HashMap<String,String>();
|
||||
|
||||
for (String f: resources) {
|
||||
String clazz =
|
||||
f.replace('/', '.').substring(0, f.length()-5);
|
||||
|
||||
Set<String> expected = loadClass(clazz);
|
||||
Set<String> found = rewriteFile(f, "{\"");
|
||||
|
||||
// This is to check that word parsing is identical to Java thinks
|
||||
if (!expected.equals(found)) {
|
||||
throw new Exception("Expected and found do not match");
|
||||
}
|
||||
|
||||
for (String name: found) {
|
||||
allnames.put(name, f);
|
||||
}
|
||||
}
|
||||
return allnames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special case treat for enums description in KeyTool
|
||||
*/
|
||||
private static Set<String> keyToolEnums() throws Exception {
|
||||
|
||||
Set<String> names = new HashSet<String>();
|
||||
|
||||
String file = "sun/security/tools/keytool/Main.java";
|
||||
System.err.println("Working on " + file);
|
||||
File origFile = new File(file);
|
||||
File tmpFile = new File(file + ".tmp");
|
||||
origFile.renameTo(tmpFile);
|
||||
tmpFile.deleteOnExit();
|
||||
|
||||
BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(new FileInputStream(tmpFile)));
|
||||
PrintWriter out = new PrintWriter(new FileOutputStream(origFile));
|
||||
|
||||
int stage = 0; // 1. commands, 2. options, 3. finished
|
||||
int match = 0;
|
||||
|
||||
while (true) {
|
||||
String s = br.readLine();
|
||||
if (s == null) {
|
||||
break;
|
||||
}
|
||||
if (s.indexOf("enum Command") >= 0) stage = 1;
|
||||
else if (s.indexOf("enum Option") >= 0) stage = 2;
|
||||
else if (s.indexOf("private static final String JKS") >= 0) stage = 3;
|
||||
|
||||
if (stage == 1 || stage == 2) {
|
||||
if (s.indexOf("(\"") >= 0) {
|
||||
match++;
|
||||
int p1, p2;
|
||||
if (stage == 1) {
|
||||
p1 = s.indexOf("\"");
|
||||
p2 = s.indexOf("\"", p1+1);
|
||||
} else {
|
||||
p2 = s.lastIndexOf("\"");
|
||||
p1 = s.lastIndexOf("\"", p2-1);
|
||||
}
|
||||
String name = s.substring(p1+1, p2);
|
||||
names.add(name);
|
||||
out.println(s.substring(0, p1+1) +
|
||||
normalize(name) +
|
||||
s.substring(p2));
|
||||
} else {
|
||||
out.println(s);
|
||||
}
|
||||
} else {
|
||||
out.println(s);
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
out.close();
|
||||
System.err.println(" GOOD match is " + match);
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a resources using JRE and returns the names
|
||||
*/
|
||||
private static Set<String> loadClass(String clazz) throws Exception {
|
||||
ListResourceBundle lrb =
|
||||
(ListResourceBundle)Class.forName(clazz).newInstance();
|
||||
Set<String> keys = lrb.keySet();
|
||||
Map<String,String> newold = new HashMap<String,String>();
|
||||
boolean dup = false;
|
||||
// Check if normalize() creates dup entries. This is crucial.
|
||||
for (String k: keys) {
|
||||
String key = normalize(k);
|
||||
if (newold.containsKey(key)) {
|
||||
err("Dup found for " + key + ":");
|
||||
err("["+newold.get(key)+"]");
|
||||
err("["+k+"]");
|
||||
dup = true;
|
||||
}
|
||||
newold.put(key, k);
|
||||
}
|
||||
if (dup) throw new Exception();
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites a file using a pattern. The name string should be right after
|
||||
* the pattern. Note: pattern ignores whitespaces. Returns names found.
|
||||
*/
|
||||
private static Set<String> rewriteFile(String file, String pattern)
|
||||
throws Exception {
|
||||
|
||||
System.err.println("Working on " + file);
|
||||
Set<String> names = new HashSet<String>();
|
||||
|
||||
int plen = pattern.length();
|
||||
int match = 0;
|
||||
|
||||
// The bare XXX.getString is also matched. Sometimes getString is
|
||||
// called but does not use literal strings. This is harder to solve.
|
||||
|
||||
int pmatch = 0;
|
||||
int pheadlen = plen - 2;
|
||||
String phead = pattern.substring(0, plen-2);
|
||||
|
||||
// The non-whitespace chars read since, used to check for pattern
|
||||
StringBuilder history = new StringBuilder();
|
||||
int hlen = 0;
|
||||
|
||||
File origFile = new File(file);
|
||||
File tmpFile = new File(file + ".tmp");
|
||||
origFile.renameTo(tmpFile);
|
||||
tmpFile.deleteOnExit();
|
||||
|
||||
FileInputStream fis = new FileInputStream(tmpFile);
|
||||
FileOutputStream fos = new FileOutputStream(origFile);
|
||||
|
||||
while (true) {
|
||||
int ch = fis.read();
|
||||
if (ch < 0) break;
|
||||
if (!Character.isWhitespace(ch)) {
|
||||
history.append((char)ch);
|
||||
hlen++;
|
||||
if (pheadlen > 0 && hlen >= pheadlen &&
|
||||
history.substring(hlen-pheadlen, hlen).equals(phead)) {
|
||||
pmatch++;
|
||||
}
|
||||
}
|
||||
|
||||
if (hlen >= plen &&
|
||||
history.substring(hlen-plen, hlen).equals(pattern)) {
|
||||
match++;
|
||||
history = new StringBuilder();
|
||||
hlen = 0;
|
||||
|
||||
fos.write(ch);
|
||||
|
||||
// Save a name
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// Save things after the second ". Maybe it's an end, maybe
|
||||
// it's just literal string concatenation.
|
||||
StringBuilder tail = new StringBuilder();
|
||||
|
||||
boolean in = true; // inside name string
|
||||
while (true) {
|
||||
int n = fis.read();
|
||||
if (in) {
|
||||
if (n == '\\') {
|
||||
int second = fis.read();
|
||||
switch (second) {
|
||||
case 'n': sb.append('\n'); break;
|
||||
case 'r': sb.append('\r'); break;
|
||||
case 't': sb.append('\t'); break;
|
||||
case '"': sb.append('"'); break;
|
||||
default: throw new Exception(String.format(
|
||||
"I don't know this escape: %s%c",
|
||||
sb.toString(), second));
|
||||
}
|
||||
} else if (n == '"') {
|
||||
in = false;
|
||||
// Maybe string concat? say bytes until clear
|
||||
tail = new StringBuilder();
|
||||
tail.append('"');
|
||||
} else {
|
||||
sb.append((char)n);
|
||||
}
|
||||
} else {
|
||||
tail.append((char)n);
|
||||
if (n == '"') { // string concat, in again
|
||||
in = true;
|
||||
} else if (n == ',' || n == ')') { // real end
|
||||
break;
|
||||
} else if (Character.isWhitespace(n) || n == '+') {
|
||||
// string concat
|
||||
} else {
|
||||
throw new Exception("Not a correct concat");
|
||||
}
|
||||
}
|
||||
}
|
||||
String s = sb.toString();
|
||||
names.add(s);
|
||||
fos.write(normalize(s).getBytes());
|
||||
fos.write(tail.toString().getBytes());
|
||||
} else {
|
||||
fos.write(ch);
|
||||
}
|
||||
}
|
||||
|
||||
// Check pheadlen > 0. Don't want to mess with rewrite for resources
|
||||
if (pheadlen > 0 && pmatch != match) {
|
||||
err(" BAD!! pmatch != match: " + pmatch + " != " + match);
|
||||
} else {
|
||||
System.err.println(" GOOD match is " + match);
|
||||
}
|
||||
|
||||
fis.close();
|
||||
fos.close();
|
||||
return names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a string. Rules:
|
||||
*
|
||||
* 1. If all spacebar return "nSPACE", n is count
|
||||
* 2. If consisting at least one alphanumeric:
|
||||
* a. All alphanumeric remain
|
||||
* b. All others in a row goes to a single ".", even if at head or tail
|
||||
* 3. Otherwise:
|
||||
* a. "****\n\n" to "STARNN", special case
|
||||
* b. the English name if first char in *,.\n():'"
|
||||
*
|
||||
* Current observations show there's no dup, Hurray! Otherwise, add more
|
||||
* special cases.
|
||||
*/
|
||||
private static String normalize(String s) throws Exception {
|
||||
boolean needDot = false;
|
||||
|
||||
// All spacebar case
|
||||
int n = 0;
|
||||
for (char c: s.toCharArray()) {
|
||||
if (c == ' ') n++;
|
||||
else n = -10000;
|
||||
}
|
||||
if (n == 1) return "SPACE";
|
||||
else if (n > 1) return "" + n + "SPACE";
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int dotpos = -1;
|
||||
for (int i=0; i<s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (Character.isLetter(c) || Character.isDigit(c) ||
|
||||
c == '{' || c == '}') {
|
||||
if (needDot) {
|
||||
// Rememeber the last dot, we want shorter form nice
|
||||
if (sb.length() <= MAXLEN) dotpos = sb.length();
|
||||
// "." only added when an alphanumeric is seen. This makes
|
||||
// sure sb is empty when there's no alphanumerics at all
|
||||
sb.append(".");
|
||||
}
|
||||
sb.append(c);
|
||||
needDot = false;
|
||||
} else {
|
||||
needDot = true;
|
||||
}
|
||||
}
|
||||
|
||||
// No alphanemeric?
|
||||
if (sb.length() == 0) {
|
||||
if (s.contains("*") && s.contains("\n")) {
|
||||
return "STARNN";
|
||||
}
|
||||
for (char c: s.toCharArray()) {
|
||||
switch (c) {
|
||||
case '*': return "STAR";
|
||||
case ',': return "COMMA";
|
||||
case '.': return "PERIOD";
|
||||
case '\n': return "NEWLINE";
|
||||
case '(': return "LPARAM";
|
||||
case ')': return "RPARAM";
|
||||
case ':': return "COLON";
|
||||
case '\'': case '"': return "QUOTE";
|
||||
}
|
||||
}
|
||||
throw new Exception("Unnamed char: [" + s + "]");
|
||||
}
|
||||
|
||||
// tail "." only added when there are alphanumerics
|
||||
if (needDot) sb.append('.');
|
||||
String res = sb.toString();
|
||||
if (res.length() > MAXLEN) {
|
||||
if (dotpos < 0) throw new Exception("No dot all over? " + s);
|
||||
return res.substring(0, dotpos);
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
private static void err(String string) {
|
||||
System.out.println("\u001b[1;37;41m" + string + "\u001b[m");
|
||||
}
|
||||
}
|
213
test/jdk/sun/security/util/Resources/Usages.java
Normal file
213
test/jdk/sun/security/util/Resources/Usages.java
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8215937
|
||||
* @modules java.base/sun.security.util
|
||||
* java.base/sun.security.tools.keytool
|
||||
* jdk.jartool/sun.security.tools.jarsigner
|
||||
* @summary Check usages of security-related Resources files
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.ListResourceBundle;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* This test checks if the strings in various Resources files are used
|
||||
* properly. Each string must be used somewhere, and each getString() call
|
||||
* must use an existing string.
|
||||
* <p>
|
||||
* For each Resources file, the test maintains a list of where the strings are
|
||||
* used (a file or a directory) and how they are used (one or more patterns).
|
||||
* <p>
|
||||
* If this test fails, there can be several reasons:
|
||||
* <p>
|
||||
* 1. If a string is not found, it has not been added to a Resources file.
|
||||
* <p>
|
||||
* 2. If a string is not used, maybe the call was removed earlier but the
|
||||
* Resources file was not updated. Or, the file is not listed or the
|
||||
* pattern is not correct and the usage is not found.
|
||||
* <p>
|
||||
* Because of #2 above, this test might not be complete. If a getString()
|
||||
* is called but either the file and calling pattern is not listed here,
|
||||
* we cannot guarantee it exists in a Resources file.
|
||||
*/
|
||||
public class Usages {
|
||||
|
||||
// src folder
|
||||
static Path SRC = Path.of(
|
||||
System.getProperty("test.src"), "../../../../../../src/")
|
||||
.normalize();
|
||||
|
||||
// rb.getString(). Used by keytool, jarsigner, and KeyStoreUtil.
|
||||
static Pattern RB_GETSTRING = Pattern.compile(
|
||||
"(?m)rb[ \\n]*\\.getString[ \\n]*\\([ \\n]*\"(.*?)\"\\)");
|
||||
|
||||
// Command and Option enums in keytool
|
||||
static Pattern KT_ENUM = Pattern.compile("\\n +[A-Z]+\\(.*\"(.*)\"");
|
||||
|
||||
// ResourceMgr.getAuthResourceString
|
||||
static Pattern GETAUTHSTRING = Pattern.compile(
|
||||
"getAuthResourceString[ \\n]*\\([ \\n]*\"(.*?)\"\\)");
|
||||
|
||||
// ResourceMgr.getString
|
||||
static Pattern MGR_GETSTRING = Pattern.compile(
|
||||
"ResourcesMgr\\.getString[ \\n]*\\([ \\n]*\"(.*?)\"\\)");
|
||||
|
||||
// LocalizedMessage.getNonlocalized("...")
|
||||
static Pattern LOC_GETNONLOC = Pattern.compile(
|
||||
"LocalizedMessage\\.getNonlocalized[ \\n]*\\([ \\n]*\"(.*?)\"");
|
||||
|
||||
// LocalizedMessage.getNonlocalized(POLICY + "...")
|
||||
static Pattern LOC_GETNONLOC_POLICY = Pattern.compile(
|
||||
"LocalizedMessage\\.getNonlocalized[ \\n]*\\([ \\n]*(POLICY \\+ \".*?)\"");
|
||||
|
||||
// new LocalizedMessage("...")
|
||||
static Pattern NEW_LOC = Pattern.compile(
|
||||
"new LocalizedMessage[ \\n]*\\([ \\n]*\"(.*?)\"");
|
||||
|
||||
// ioException in ConfigFile.java
|
||||
static Pattern IOEXCEPTION = Pattern.compile(
|
||||
"ioException[ \\n]*\\([ \\n]*\"(.*?)\",");
|
||||
|
||||
// For each Resources file, where and how the strings are used.
|
||||
static Map<ListResourceBundle, List<Pair>> MAP = Map.of(
|
||||
new sun.security.tools.keytool.Resources(), List.of(
|
||||
new Pair("java.base/share/classes/sun/security/tools/keytool/Main.java",
|
||||
List.of(RB_GETSTRING, KT_ENUM)),
|
||||
new Pair("java.base/share/classes/sun/security/tools/KeyStoreUtil.java",
|
||||
List.of(RB_GETSTRING))),
|
||||
new sun.security.util.AuthResources(), List.of(
|
||||
new Pair("java.base/share/classes/sun/security/provider/ConfigFile.java",
|
||||
List.of(GETAUTHSTRING, IOEXCEPTION)),
|
||||
new Pair("jdk.security.auth/share/classes/com/sun/security/auth/",
|
||||
List.of(GETAUTHSTRING))),
|
||||
new sun.security.tools.jarsigner.Resources(), List.of(
|
||||
new Pair("jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java",
|
||||
List.of(RB_GETSTRING)),
|
||||
new Pair("java.base/share/classes/sun/security/tools/KeyStoreUtil.java",
|
||||
List.of(RB_GETSTRING))),
|
||||
new sun.security.util.Resources(), List.of(
|
||||
new Pair("jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java",
|
||||
List.of(MGR_GETSTRING)),
|
||||
new Pair("java.base/share/classes/sun/security/provider/PolicyParser.java",
|
||||
List.of(LOC_GETNONLOC, NEW_LOC)),
|
||||
new Pair("java.base/share/classes/sun/security/provider/PolicyFile.java",
|
||||
List.of(MGR_GETSTRING, LOC_GETNONLOC, LOC_GETNONLOC_POLICY)),
|
||||
new Pair("java.base/share/classes/javax/security/auth/",
|
||||
List.of(MGR_GETSTRING)))
|
||||
);
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (Files.exists(SRC)) {
|
||||
MAP.forEach(Usages::check);
|
||||
} else {
|
||||
System.out.println("No src directory. Test skipped.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void check(ListResourceBundle res, List<Pair> fnps) {
|
||||
try {
|
||||
System.out.println(">>>> Checking " + res.getClass().getName());
|
||||
|
||||
List<String> keys = Collections.list(res.getKeys());
|
||||
|
||||
// Initialize unused to be all keys. Each time a key is used it
|
||||
// is removed. We cannot reuse keys because a key might be used
|
||||
// multiple times. Make it a Set so we can check duplicates.
|
||||
Set<String> unused = new HashSet<>(keys);
|
||||
|
||||
keys.forEach(Usages::checkKeyFormat);
|
||||
if (keys.size() != unused.size()) {
|
||||
throw new RuntimeException("Duplicates found");
|
||||
}
|
||||
|
||||
for (Pair fnp : fnps) {
|
||||
Files.find(SRC.resolve(fnp.path), Integer.MAX_VALUE,
|
||||
(p, attr) -> p.toString().endsWith(".java"))
|
||||
.forEach(pa -> {
|
||||
try {
|
||||
String content = Files.readString(pa);
|
||||
for (Pattern p : fnp.patterns) {
|
||||
Matcher m = p.matcher(content);
|
||||
while (m.find()) {
|
||||
String arg = m.group(1);
|
||||
// Special case in PolicyFile.java:
|
||||
if (arg.startsWith("POLICY + \"")) {
|
||||
arg = "java.security.policy"
|
||||
+ arg.substring(10);
|
||||
}
|
||||
if (!keys.contains(arg)) {
|
||||
throw new RuntimeException(
|
||||
"Not found: " + arg);
|
||||
}
|
||||
unused.remove(arg);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!unused.isEmpty()) {
|
||||
throw new RuntimeException("Unused keys: " + unused);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkKeyFormat(String key) {
|
||||
for (char c : key.toCharArray()) {
|
||||
if (Character.isLetter(c) || Character.isDigit(c) ||
|
||||
c == '{' || c == '}' || c == '.') {
|
||||
// OK
|
||||
} else {
|
||||
throw new RuntimeException(
|
||||
"Illegal char [" + c + "] in key: " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Pair {
|
||||
|
||||
public final String path;
|
||||
public final List<Pattern> patterns;
|
||||
|
||||
public Pair(String path, List<Pattern> patterns) {
|
||||
this.path = path;
|
||||
this.patterns = patterns;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user