8168075: Custom system class loader + security manager + malformed policy file = recursive initialization

Reviewed-by: mchung, mullan
This commit is contained in:
Adam Petcher 2017-01-24 16:19:21 -05:00
parent cf31932ce4
commit f3f3725fdd
7 changed files with 362 additions and 76 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2017, 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
@ -32,7 +32,6 @@ import java.net.URL;
import java.net.URI;
import java.nio.file.Paths;
import java.util.*;
import java.text.MessageFormat;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
@ -579,10 +578,9 @@ public class PolicyFile extends java.security.Policy {
k.add(policy);
return k;
});
MessageFormat form = new MessageFormat(ResourcesMgr.getString
(POLICY + ".error.parsing.policy.message"));
Object[] source = {policy, pe.getLocalizedMessage()};
System.err.println(form.format(source));
System.err.println(LocalizedMessage.getMessage
(POLICY + ".error.parsing.policy.message", source));
if (debug != null) {
pe.printStackTrace();
}
@ -805,32 +803,30 @@ public class PolicyFile extends java.security.Policy {
}
}
} catch (java.lang.reflect.InvocationTargetException ite) {
MessageFormat form = new MessageFormat
(ResourcesMgr.getString
(POLICY +
".error.adding.Permission.perm.message"));
Object[] source = {pe.permission,
ite.getTargetException().toString()};
System.err.println(form.format(source));
System.err.println(
LocalizedMessage.getMessage(
POLICY + ".error.adding.Permission.perm.message",
source));
} catch (Exception e) {
MessageFormat form = new MessageFormat
(ResourcesMgr.getString
(POLICY +
".error.adding.Permission.perm.message"));
Object[] source = {pe.permission,
e.toString()};
System.err.println(form.format(source));
System.err.println(
LocalizedMessage.getMessage(
POLICY + ".error.adding.Permission.perm.message",
source));
}
}
// No need to sync because noone has access to newInfo yet
newInfo.policyEntries.add(entry);
} catch (Exception e) {
MessageFormat form = new MessageFormat(ResourcesMgr.getString
(POLICY
+ ".error.adding.Entry.message"));
Object[] source = {e.toString()};
System.err.println(form.format(source));
System.err.println(
LocalizedMessage.getMessage(
POLICY + ".error.adding.Entry.message",
source));
}
if (debug != null)
debug.println();
@ -1803,29 +1799,29 @@ public class PolicyFile extends java.security.Policy {
} else if (prefix.equalsIgnoreCase("alias")) {
// get the suffix and perform keystore alias replacement
if (colonIndex == -1) {
MessageFormat form = new MessageFormat
(ResourcesMgr.getString
("alias.name.not.provided.pe.name."));
Object[] source = {pe.name};
throw new Exception(form.format(source));
throw new Exception(
LocalizedMessage.getMessage(
"alias.name.not.provided.pe.name.",
source));
}
suffix = value.substring(colonIndex+1);
if ((suffix = getDN(suffix, keystore)) == null) {
MessageFormat form = new MessageFormat
(ResourcesMgr.getString
("unable.to.perform.substitution.on.alias.suffix"));
Object[] source = {value.substring(colonIndex+1)};
throw new Exception(form.format(source));
throw new Exception(
LocalizedMessage.getMessage(
"unable.to.perform.substitution.on.alias.suffix",
source));
}
sb.append(X500PRINCIPAL + " \"" + suffix + "\"");
startIndex = e+2;
} else {
MessageFormat form = new MessageFormat
(ResourcesMgr.getString
("substitution.value.prefix.unsupported"));
Object[] source = {prefix};
throw new Exception(form.format(source));
throw new Exception(
LocalizedMessage.getMessage(
"substitution.value.prefix.unsupported",
source));
}
}
@ -2039,7 +2035,7 @@ public class PolicyFile extends java.security.Policy {
super(type);
if (type == null) {
throw new NullPointerException
(ResourcesMgr.getString("type.can.t.be.null"));
(LocalizedMessage.getMessage("type.can.t.be.null"));
}
this.type = type;
this.name = name;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2017, 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
@ -26,18 +26,14 @@
package sun.security.provider;
import java.io.*;
import java.lang.RuntimePermission;
import java.net.SocketPermission;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.Principal;
import java.text.MessageFormat;
import java.util.*;
import javax.security.auth.x500.X500Principal;
import sun.security.util.Debug;
import sun.security.util.PropertyExpander;
import sun.security.util.ResourcesMgr;
import sun.security.util.LocalizedMessage;
/**
* The policy for a Java runtime (specifying
@ -209,13 +205,12 @@ public class PolicyParser {
if (!domainEntries.containsKey(domainName)) {
domainEntries.put(domainName, de);
} else {
MessageFormat form =
new MessageFormat(ResourcesMgr.getString(
"duplicate.keystore.domain.name"));
LocalizedMessage localizedMsg =
new LocalizedMessage("duplicate.keystore.domain.name");
Object[] source = {domainName};
String msg = "duplicate keystore domain name: " +
domainName;
throw new ParsingException(msg, form, source);
throw new ParsingException(msg, localizedMsg, source);
}
}
} else {
@ -225,7 +220,7 @@ public class PolicyParser {
}
if (keyStoreUrlString == null && storePassURL != null) {
throw new ParsingException(ResourcesMgr.getString
throw new ParsingException(LocalizedMessage.getMessage
("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore"));
}
}
@ -367,7 +362,7 @@ public class PolicyParser {
keyStoreType = match("quoted string");
} else {
throw new ParsingException(st.lineno(),
ResourcesMgr.getString("expected.keystore.type"));
LocalizedMessage.getMessage("expected.keystore.type"));
}
// parse keystore provider
@ -380,7 +375,7 @@ public class PolicyParser {
keyStoreProvider = match("quoted string");
} else {
throw new ParsingException(st.lineno(),
ResourcesMgr.getString("expected.keystore.provider"));
LocalizedMessage.getMessage("expected.keystore.provider"));
}
}
@ -430,7 +425,7 @@ public class PolicyParser {
if (e.codeBase != null)
throw new ParsingException(
st.lineno(),
ResourcesMgr.getString
LocalizedMessage.getMessage
("multiple.Codebase.expressions"));
e.codeBase = match("quoted string");
peekAndMatch(",");
@ -438,8 +433,8 @@ public class PolicyParser {
if (e.signedBy != null)
throw new ParsingException(
st.lineno(),
ResourcesMgr.getString(
"multiple.SignedBy.expressions"));
LocalizedMessage.getMessage
("multiple.SignedBy.expressions"));
e.signedBy = match("quoted string");
// verify syntax of the aliases
@ -457,8 +452,8 @@ public class PolicyParser {
if (actr <= cctr)
throw new ParsingException(
st.lineno(),
ResourcesMgr.getString(
"SignedBy.has.empty.alias"));
LocalizedMessage.getMessage
("SignedBy.has.empty.alias"));
peekAndMatch(",");
} else if (peekAndMatch("Principal")) {
@ -500,7 +495,7 @@ public class PolicyParser {
}
throw new ParsingException
(st.lineno(),
ResourcesMgr.getString
LocalizedMessage.getMessage
("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name"));
}
}
@ -537,8 +532,8 @@ public class PolicyParser {
} else {
throw new ParsingException(st.lineno(),
ResourcesMgr.getString(
"expected.codeBase.or.SignedBy.or.Principal"));
LocalizedMessage.getMessage
("expected.codeBase.or.SignedBy.or.Principal"));
}
}
@ -561,8 +556,8 @@ public class PolicyParser {
} else {
throw new
ParsingException(st.lineno(),
ResourcesMgr.getString(
"expected.permission.entry"));
LocalizedMessage.getMessage
("expected.permission.entry"));
}
}
match("}");
@ -738,15 +733,14 @@ public class PolicyParser {
switch (lookahead) {
case StreamTokenizer.TT_NUMBER:
throw new ParsingException(st.lineno(), expect,
ResourcesMgr.getString("number.") +
String.valueOf(st.nval));
LocalizedMessage.getMessage("number.") +
String.valueOf(st.nval));
case StreamTokenizer.TT_EOF:
MessageFormat form = new MessageFormat(
ResourcesMgr.getString
("expected.expect.read.end.of.file."));
LocalizedMessage localizedMsg = new LocalizedMessage
("expected.expect.read.end.of.file.");
Object[] source = {expect};
String msg = "expected [" + expect + "], read [end of file]";
throw new ParsingException(msg, form, source);
throw new ParsingException(msg, localizedMsg, source);
case StreamTokenizer.TT_WORD:
if (expect.equalsIgnoreCase(st.sval)) {
lookahead = st.nextToken();
@ -832,10 +826,10 @@ public class PolicyParser {
switch (lookahead) {
case StreamTokenizer.TT_NUMBER:
throw new ParsingException(st.lineno(), ";",
ResourcesMgr.getString("number.") +
String.valueOf(st.nval));
LocalizedMessage.getMessage("number.") +
String.valueOf(st.nval));
case StreamTokenizer.TT_EOF:
throw new ParsingException(ResourcesMgr.getString
throw new ParsingException(LocalizedMessage.getMessage
("expected.read.end.of.file."));
default:
lookahead = st.nextToken();
@ -993,8 +987,8 @@ public class PolicyParser {
*/
public PrincipalEntry(String principalClass, String principalName) {
if (principalClass == null || principalName == null)
throw new NullPointerException(ResourcesMgr.getString(
"null.principalClass.or.principalName"));
throw new NullPointerException(LocalizedMessage.getMessage
("null.principalClass.or.principalName"));
this.principalClass = principalClass;
this.principalName = principalName;
}
@ -1244,11 +1238,11 @@ public class PolicyParser {
if (!entries.containsKey(keystoreName)) {
entries.put(keystoreName, entry);
} else {
MessageFormat form = new MessageFormat(ResourcesMgr.getString(
"duplicate.keystore.name"));
LocalizedMessage localizedMsg = new LocalizedMessage
("duplicate.keystore.name");
Object[] source = {keystoreName};
String msg = "duplicate keystore name: " + keystoreName;
throw new ParsingException(msg, form, source);
throw new ParsingException(msg, localizedMsg, source);
}
}
@ -1320,7 +1314,7 @@ public class PolicyParser {
private static final long serialVersionUID = -4330692689482574072L;
private String i18nMessage;
private MessageFormat form;
private LocalizedMessage localizedMsg;
private Object[] source;
/**
@ -1336,10 +1330,10 @@ public class PolicyParser {
i18nMessage = msg;
}
public ParsingException(String msg, MessageFormat form,
public ParsingException(String msg, LocalizedMessage localizedMsg,
Object[] source) {
super(msg);
this.form = form;
this.localizedMsg = localizedMsg;
this.source = source;
}
@ -1347,7 +1341,7 @@ public class PolicyParser {
super("line " + line + ": " + msg);
// don't call form.format unless getLocalizedMessage is called
// to avoid unnecessary permission checks
form = new MessageFormat(ResourcesMgr.getString("line.number.msg"));
localizedMsg = new LocalizedMessage("line.number.msg");
source = new Object[] {line, msg};
}
@ -1356,14 +1350,14 @@ public class PolicyParser {
"], found [" + actual + "]");
// don't call form.format unless getLocalizedMessage is called
// to avoid unnecessary permission checks
form = new MessageFormat(ResourcesMgr.getString
("line.number.expected.expect.found.actual."));
localizedMsg = new LocalizedMessage
("line.number.expected.expect.found.actual.");
source = new Object[] {line, expect, actual};
}
@Override
public String getLocalizedMessage() {
return i18nMessage != null ? i18nMessage : form.format(source);
return i18nMessage != null ? i18nMessage : localizedMsg.format(source);
}
}

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) 2017, 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 sun.security.util;
/**
* This class produces formatted and localized messages describing security
* issues. Some messages may be required when the VM is not fully booted. In
* this case, localization resources and classes used for message formatting
* may not be available. When the VM is not booted, the message will not be
* localized, and it will be formatted using simplified message formatting
* code that is contained in this class.
*/
/*
* Some of this code is executed before the VM is fully booted. Some import
* statements have been omitted to help prevent accidental use of classes that
* may not be available during boot.
*/
public class LocalizedMessage {
private static final Resources resources = new Resources();
private final String key;
/**
* A LocalizedMessage can be instantiated with a key and formatted with
* arguments later in the style of MessageFormat. This organization
* allows the actual formatting (and associated permission checks) to be
* avoided unless the resulting string is needed.
* @param key
*/
public LocalizedMessage(String key) {
this.key = key;
}
/**
* Return a localized string corresponding to the key stored in this
* object, formatted with the provided arguments. When the VM is booted,
* this method will obtain the correct localized message and format it
* using java.text.MessageFormat. Otherwise, a non-localized string is
* returned, and the formatting is performed by simplified formatting code.
*
* @param arguments The arguments that should be placed in the message
* @return A formatted message string
*/
public String format(Object... arguments) {
return getMessage(key, arguments);
}
/**
* Return a non-localized string corresponding to the provided key, and
* formatted with the provided arguments. All strings are obtained from
* sun.security.util.Resources, and the formatting only supports
* simple positional argument replacement (e.g. {1}).
*
* @param key The key of the desired string in Resources
* @param arguments The arguments that should be placed in the message
* @return A formatted message string
*/
public static String getMessageUnbooted(String key,
Object... arguments) {
String value = resources.getString(key);
if (arguments == null || arguments.length == 0) {
return value;
}
// Classes like StringTokenizer may not be loaded, so parsing
// is performed with String methods
StringBuilder sb = new StringBuilder();
int nextBraceIndex;
while ((nextBraceIndex = value.indexOf('{')) >= 0) {
String firstPart = value.substring(0, nextBraceIndex);
sb.append(firstPart);
value = value.substring(nextBraceIndex + 1);
// look for closing brace and argument index
nextBraceIndex = value.indexOf('}');
if (nextBraceIndex < 0) {
// no closing brace
// MessageFormat would throw IllegalArgumentException, but
// that exception class may not be loaded yet
throw new RuntimeException("Unmatched braces");
}
String indexStr = value.substring(0, nextBraceIndex);
try {
int index = Integer.parseInt(indexStr);
sb.append(arguments[index]);
}
catch(NumberFormatException e) {
// argument index is not an integer
throw new RuntimeException("not an integer: " + indexStr);
}
value = value.substring(nextBraceIndex + 1);
}
sb.append(value);
return sb.toString();
}
/**
* Return a localized string corresponding to the provided key, and
* formatted with the provided arguments. When the VM is booted, this
* method will obtain the correct localized message and format it using
* java.text.MessageFormat. Otherwise, a non-localized string is returned,
* and the formatting is performed by simplified formatting code.
*
* @param key The key of the desired string in the security resource bundle
* @param arguments The arguments that should be placed in the message
* @return A formatted message string
*/
public static String getMessage(String key,
Object... arguments) {
if (jdk.internal.misc.VM.isBooted()) {
// Localization and formatting resources are available
String value = ResourcesMgr.getString(key);
if (arguments == null) {
return value;
}
java.text.MessageFormat form = new java.text.MessageFormat(value);
return form.format(arguments);
} else {
return getMessageUnbooted(key, arguments);
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2017, 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 8168075
* @summary Ensure that security messages can be formatted during system class
* loader initialization.
* @build CustomClassLoader
* @run main/othervm/java.security.policy=error.policy -Djava.security.manager -Djava.system.class.loader=CustomClassLoader BootMessages
*/
public class BootMessages {
public static void main(String[] args) throws Exception {
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2017, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/*
* A class loader that can be used in tests that require a custom class
* loader. Its behavior is identical to ClassLoader.
*/
public class CustomClassLoader extends ClassLoader {
public CustomClassLoader(ClassLoader parent) {
super(parent);
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2017, 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.util.*;
import sun.security.util.Resources;
import sun.security.util.LocalizedMessage;
import java.text.MessageFormat;
/*
* @test
* @bug 8168075
* @summary Ensure that security message formatting code is capable of
* displaying all messages.
* @modules java.base/sun.security.util
*/
public class MessageFormatting {
private static final Object[] MSG_ARGS = new Integer[]{0, 1, 2};
public static void main(String[] args) throws Exception {
Resources resources = new Resources();
Enumeration<String> keys = resources.getKeys();
while (keys.hasMoreElements()) {
String curKey = keys.nextElement();
String formattedString = LocalizedMessage.getMessageUnbooted(curKey, MSG_ARGS);
String msg = resources.getString(curKey);
String expectedString = formatIfNecessary(msg, MSG_ARGS);
if (!formattedString.equals(expectedString)) {
System.err.println("Expected string:");
System.err.println(expectedString);
System.err.println("Actual string:");
System.err.println(formattedString);
throw new Exception("Incorrect message string");
}
}
}
private static String formatIfNecessary(String str, Object[] args) {
// message formatting code only formats messages with arguments
if (str.indexOf('{') < 0) {
return str;
}
MessageFormat format = new MessageFormat(str);
return format.format(args);
}
}

View File

@ -0,0 +1,5 @@
grant {
permission java.lang.RuntimePermission "createClassLoader";
permission java.lang.RuntimePermission "";
};