blob: 3810f38d878fde660a51b8cb3162fb7a6ec17aff [file] [log] [blame]
/*
* Copyright (c) 2016, 2019, 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 static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.SerializablePermission;
import java.security.Security;
import java.util.Objects;
import org.testng.Assert;
import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
import sun.misc.ObjectInputFilter;
/* @test
* @bug 8231422
* @build GlobalFilterTest SerialFilterTest
* @run testng/othervm GlobalFilterTest
* @run testng/othervm -Djdk.serialFilter=java.**
* -Dexpected-jdk.serialFilter=java.** GlobalFilterTest
* @run testng/othervm/policy=security.policy GlobalFilterTest
* @run testng/othervm/policy=security.policy
* -Djava.security.properties=${test.src}/java.security-extra1
* -Djava.security.debug=properties GlobalFilterTest
*
* @summary Test Global Filters
*/
@Test
public class GlobalFilterTest {
private static final String serialPropName = "jdk.serialFilter";
private static final String badSerialFilter = "java.lang.StringBuffer;!*";
private static final String origSerialFilterProperty =
System.setProperty(serialPropName, badSerialFilter);
/**
* DataProvider of patterns and objects derived from the configured process-wide filter.
* @return Array of arrays of pattern, object, allowed boolean, and API factory
*/
@DataProvider(name="globalPatternElements")
Object[][] globalPatternElements() {
String globalFilter =
System.getProperty("expected-" + serialPropName,
Security.getProperty(serialPropName));
if (globalFilter == null) {
return new Object[0][];
}
String[] patterns = globalFilter.split(";");
Object[][] objects = new Object[patterns.length][];
for (int i = 0; i < patterns.length; i++) {
Object o;
boolean allowed;
String pattern = patterns[i].trim();
if (pattern.contains("=")) {
allowed = false;
o = SerialFilterTest.genTestObject(pattern, false);
} else {
allowed = !pattern.startsWith("!");
o = (allowed)
? SerialFilterTest.genTestObject(pattern, true)
: SerialFilterTest.genTestObject(pattern.substring(1), false);
Assert.assertNotNull(o, "fail generation failed");
}
objects[i] = new Object[3];
objects[i][0] = pattern;
objects[i][1] = allowed;
objects[i][2] = o;
}
return objects;
}
/**
* Test that the process-wide filter is set when the properties are set
* and has the toString matching the configured pattern.
*/
@Test()
static void globalFilter() {
ObjectInputFilter filter = ObjectInputFilter.Config.getSerialFilter();
// Check that the System.setProperty(jdk.serialFilter) DOES NOT affect the filter.
String asSetSystemProp = System.getProperty(serialPropName,
Security.getProperty(serialPropName));
Assert.assertNotEquals(Objects.toString(filter, null), asSetSystemProp,
"System.setProperty(\"jdk.serialfilter\", ...) should not change filter: " +
asSetSystemProp);
String pattern =
System.getProperty("expected-" + serialPropName,
Security.getProperty(serialPropName));
System.out.printf("global pattern: %s, filter: %s%n", pattern, filter);
Assert.assertEquals(Objects.toString(filter, null), pattern,
"process-wide filter pattern does not match");
}
/**
* If the Global filter is already set, it should always refuse to be
* set again.
* If there is a security manager, setting the serialFilter should fail
* without the appropriate permission.
* If there is no security manager then setting it should work.
*/
@Test()
static void setGlobalFilter() {
SecurityManager sm = System.getSecurityManager();
ObjectInputFilter filter = new SerialFilterTest.Validator();
ObjectInputFilter global = ObjectInputFilter.Config.getSerialFilter();
if (global != null) {
// once set, can never be re-set
try {
ObjectInputFilter.Config.setSerialFilter(filter);
Assert.fail("set only once process-wide filter");
} catch (IllegalStateException ise) {
if (sm != null) {
Assert.fail("wrong exception when security manager is set", ise);
}
} catch (SecurityException se) {
if (sm == null) {
Assert.fail("wrong exception when security manager is not set", se);
}
}
} else {
if (sm == null) {
// no security manager
try {
ObjectInputFilter.Config.setSerialFilter(filter);
// Note once set, it can not be reset; so other tests
System.out.printf("Global Filter set to Validator%n");
} catch (SecurityException se) {
Assert.fail("setGlobalFilter should not get SecurityException", se);
}
try {
// Try to set it again, expecting it to throw
ObjectInputFilter.Config.setSerialFilter(filter);
Assert.fail("set only once process-wide filter");
} catch (IllegalStateException ise) {
// Normal case
}
} else {
// Security manager
SecurityException expectSE = null;
try {
sm.checkPermission(new SerializablePermission("serialFilter"));
} catch (SecurityException se1) {
expectSE = se1;
}
SecurityException actualSE = null;
try {
ObjectInputFilter.Config.setSerialFilter(filter);
} catch (SecurityException se2) {
actualSE = se2;
}
if (expectSE == null | actualSE == null) {
Assert.assertEquals(expectSE, actualSE, "SecurityException");
} else {
Assert.assertEquals(expectSE.getClass(), actualSE.getClass(),
"SecurityException class");
}
}
}
}
/**
* For each pattern in the process-wide filter test a generated object
* against the default process-wide filter.
*
* @param pattern a pattern extracted from the configured global pattern
*/
@Test(dataProvider = "globalPatternElements")
static void globalFilterElements(String pattern, boolean allowed,Object obj) {
testGlobalPattern(pattern, obj, allowed);
}
/**
* Serialize and deserialize an object using the default process-wide filter
* and check allowed or reject.
*
* @param pattern the pattern
* @param object the test object
* @param allowed the expected result from ObjectInputStream (exception or not)
*/
static void testGlobalPattern(String pattern, Object object, boolean allowed) {
try {
// System.out.printf("global %s pattern: %s, obj: %s%n", (allowed ? "allowed" : "not allowed"), pattern, object);
byte[] bytes = SerialFilterTest.writeObjects(object);
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais)) {
Object o = ois.readObject();
} catch (EOFException eof) {
// normal completion
} catch (ClassNotFoundException cnf) {
Assert.fail("Deserializing", cnf);
}
Assert.assertTrue(allowed, "filter should have thrown an exception");
} catch (IllegalArgumentException iae) {
Assert.fail("bad format pattern", iae);
} catch (InvalidClassException ice) {
Assert.assertFalse(allowed, "filter should not have thrown an exception: " + ice);
} catch (IOException ioe) {
Assert.fail("Unexpected IOException", ioe);
}
}
}