Skip to content

Commit

Permalink
GEODE-3923 Provide whitelist/blacklist capability for java serialization
Browse files Browse the repository at this point in the history
This is a squashed and rebased commit of the work from the now-defunct
whitelist-wip branch.  The work allows you to whitelist and blacklist
use of Java deserialization for classes matching a pattern you provide.

All distributedTests are now run with this enabled and with the default
blacklist pattern of "!*", which blacklists everything not explicitely
allowed by Geode in the sanctionedSerializables.txt files or by your test.

Each layer needing one now has its own sanctionedSerializables.txt file
that is a resource in the product tree.  These are installed as whitelists
by a new DistributedSystemService in that layer.

There are numerous examples of whitelisting classes in the tests.

If you do not whitelist your class Geode will throw an IncompatibleClassException.
If you happen to run into this look in your logs for "rejecting" and you
can easily find the name of the offending class and add it to your
whitelist.

Two new cache properties have been added:

  validate-serializable-objects

If true checks incoming java serializable objects against a filter (allows
internal Geode classes and any others provided in the
serializable-object-filter property).

If you enable this property you must be using Java 8 build 121 or later.
If you are not Geode will throw an exception and refuse to start.

Default: "false"

  serializable-object-filter

A user provided whitelist of objects that the system will allow to serialize.
See java.io.ObjectInputFilter.Config for details on the syntax for creating filters.

https://docs.oracle.com/javase/9/docs/api/java/io/ObjectInputFilter.Config.html

Default: "!*"

In addition, this work includes these significant contributions:

commit 731ac2b
Author: Galen O'Sullivan <[email protected]>
Date:   Wed Nov 15 10:55:23 2017 -0800

    [edited message] Allow Geode to run with older versions of Java 8
    when serialization filtering is not enabled.

    and achieve this by moving references to ObjectInputFilter out of
    InternalDataSerializer.

    Signed-off-by: Dan Smith <[email protected]>

commit 3329467
Author: Dan Smith <[email protected]>
Date:   Thu Nov 2 13:22:42 2017 -0700

    Adding an option to run tests with a different JVM

commit a480931
Author: Bruce Schuchardt <[email protected]>
Date:   Fri Nov 10 15:46:11 2017 -0800

    whitelist WIP

    Turned on validation by default for dunit tests and fixed issues this caused.
    Several tests now set a custom whitelist.

commit 026ae92
Author: Galen O'Sullivan <[email protected]>
Date:   Thu Nov 9 17:02:56 2017 -0800

    Adding configuration parameters to toggle whitelist behavior of serializable classes.

    Open question about removing DataCommandRequest.java toData and fromData

    Signed-off-by: Brian Rowe <[email protected]>
  • Loading branch information
bschuchardt committed Nov 30, 2017
1 parent d65763e commit a2bd578
Show file tree
Hide file tree
Showing 148 changed files with 2,834 additions and 693 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
*/
package org.apache.geode.modules.util;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.List;
import java.util.Set;

import org.apache.geode.DataSerializable;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.execute.Execution;
Expand All @@ -29,7 +33,7 @@
import org.apache.geode.distributed.internal.MembershipListener;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;

public class BootstrappingFunction implements Function, MembershipListener {
public class BootstrappingFunction implements Function, MembershipListener, DataSerializable {

private static final long serialVersionUID = 1856043174458190605L;

Expand All @@ -38,6 +42,8 @@ public class BootstrappingFunction implements Function, MembershipListener {
private static final int TIME_TO_WAIT_FOR_CACHE =
Integer.getInteger("gemfiremodules.timeToWaitForCache", 30000);

public BootstrappingFunction() {}

@Override
public void execute(FunctionContext context) {
// Verify that the cache exists before continuing.
Expand Down Expand Up @@ -182,4 +188,10 @@ public void memberSuspect(InternalDistributedMember id, InternalDistributedMembe
@Override
public void quorumLost(Set<InternalDistributedMember> internalDistributedMembers,
List<InternalDistributedMember> internalDistributedMembers2) {}

@Override
public void toData(DataOutput out) throws IOException {}

@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
*/
package org.apache.geode.modules.util;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;

import javax.xml.crypto.Data;

import org.apache.geode.DataSerializable;
import org.apache.geode.InternalGemFireError;
import org.apache.geode.cache.AttributesFactory;
import org.apache.geode.cache.Cache;
Expand All @@ -42,7 +47,7 @@
import org.apache.geode.internal.cache.xmlcache.CacheXmlGenerator;
import org.apache.geode.internal.i18n.LocalizedStrings;

public class CreateRegionFunction implements Function, Declarable {
public class CreateRegionFunction implements Function, Declarable, DataSerializable {

private static final long serialVersionUID = -9210226844302128969L;

Expand Down Expand Up @@ -259,4 +264,14 @@ private DistributedLockService initializeDistributedLockService(String dlsName)
}
return lockService;
}

@Override
public void toData(DataOutput out) throws IOException {

}

@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,25 @@
*/
package org.apache.geode.modules.util;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Properties;

import org.apache.geode.DataSerializable;
import org.apache.geode.cache.Declarable;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.cache.execute.FunctionContext;
import org.apache.geode.cache.execute.RegionFunctionContext;

public class RegionSizeFunction implements Function, Declarable {
public class RegionSizeFunction implements Function, Declarable, DataSerializable {

private static final long serialVersionUID = -2791590491585777990L;

public static final String ID = "region-size-function";

public RegionSizeFunction() {}

public void execute(FunctionContext context) {
RegionFunctionContext rfc = (RegionFunctionContext) context;
context.getResultSender().lastResult(rfc.getDataSet().size());
Expand All @@ -50,4 +56,14 @@ public boolean isHA() {

@Override
public void init(Properties arg0) {}

@Override
public void toData(DataOutput out) throws IOException {

}

@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
*/
package org.apache.geode.modules.util;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;

import org.apache.geode.DataSerializable;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Declarable;
Expand All @@ -30,7 +34,8 @@
* Touches the keys contained in the set of keys by performing a get on the partitioned region.
*
*/
public class TouchPartitionedRegionEntriesFunction implements Function, Declarable {
public class TouchPartitionedRegionEntriesFunction
implements Function, Declarable, DataSerializable {

private static final long serialVersionUID = -3700389655056961153L;

Expand Down Expand Up @@ -88,4 +93,14 @@ public boolean hasResult() {
}

public void init(Properties properties) {}

@Override
public void toData(DataOutput out) throws IOException {

}

@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
*/
package org.apache.geode.modules.util;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;

import org.apache.geode.DataSerializable;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.Declarable;
Expand All @@ -29,7 +33,8 @@
* is a non-data-aware function invoked using onMembers or onServers.
*
*/
public class TouchReplicatedRegionEntriesFunction implements Function, Declarable {
public class TouchReplicatedRegionEntriesFunction
implements Function, Declarable, DataSerializable {

private static final long serialVersionUID = -7424895036162243564L;

Expand Down Expand Up @@ -85,4 +90,14 @@ public boolean hasResult() {
}

public void init(Properties properties) {}

@Override
public void toData(DataOutput out) throws IOException {

}

@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public static class Health implements java.io.Serializable {
//////////////////// Instance Fields ////////////////////

/** The string for this health */
private String healthString;
private String healthString = OKAY_STRING;

///////////////////// Constructors //////////////////////

Expand All @@ -184,6 +184,9 @@ protected Health(String healthString) {
* Returns the appropriate canonical instance of <code>Health</code>.
*/
public Object readResolve() {
if (this.healthString == null) {
return null;
}
if (this.healthString.equals(GOOD_STRING)) {
return GemFireHealth.GOOD_HEALTH;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,21 +254,9 @@ protected void fireConfigurationParameterValueChanged(ConfigurationParameter par
*/
protected void setInternalState(String description, Object value, Class type,
boolean userModifiable) {
if (description == null || description.length() == 0) {
throw new IllegalArgumentException(
LocalizedStrings.ConfigurationParameterImpl_CONFIGURATIONPARAMETER_DESCRIPTION_MUST_BE_SPECIFIED
.toLocalizedString());
}
this.description = description;
this.type = type;
this.userModifiable = userModifiable;

if (value == null) {
throw new IllegalArgumentException(
LocalizedStrings.ConfigurationParameterImpl_UNABLE_TO_SET_0_TO_NULL_VALUE
.toLocalizedString(getName()));
}

this.value = value;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,6 @@ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassN
Class inClass = (Class) in.readObject();
boolean inUserModifiable = in.readBoolean();

Assert.assertTrue(inName != null);
Assert.assertTrue(inDescription != null);
Assert.assertTrue(inValue != null);
Assert.assertTrue(inClass != null);

this.deserialized = true;
this.name = inName;
setInternalState(inDescription, inValue, inClass, inUserModifiable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,12 @@ public Set getMissingRoles() {
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
// transform roles to string names which are serializable...
Set roleNames = new HashSet(this.missingRoles.size());
for (Iterator iter = this.missingRoles.iterator(); iter.hasNext();) {
Set mr = this.missingRoles;
if (mr == null) {
mr = Collections.EMPTY_SET;
}
Set roleNames = new HashSet(mr.size());
for (Iterator iter = mr.iterator(); iter.hasNext();) {
String name = ((Role) iter.next()).getName();
roleNames.add(name);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,12 @@ public Set getFailedRoles() {
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
// transform roles to string names which are serializable...
Set roleNames = new HashSet(this.failedRoles.size());
for (Iterator iter = this.failedRoles.iterator(); iter.hasNext();) {
Set fr = this.failedRoles;
if (fr == null) {
fr = Collections.EMPTY_SET;
}
Set roleNames = new HashSet(fr.size());
for (Iterator iter = fr.iterator(); iter.hasNext();) {
String name = ((Role) iter.next()).getName();
roleNames.add(name);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@

package org.apache.geode.cache.query.internal.parse;

import antlr.*;
import antlr.CommonAST;
import antlr.Token;

import org.apache.geode.cache.query.internal.QCompiler;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2070,4 +2070,31 @@ public interface ConfigurationProperties {
* <U>Since</U>: Geode 1.0
*/
String SSL_WEB_SERVICE_REQUIRE_AUTHENTICATION = "ssl-web-require-authentication";
/**
* The static String definition of the <i>"validate-serializable-objects"</i> property
*
* <U>Description</U>If true checks incoming java serializable objects against a filter (allows
* internal Geode classes and any others provided in the serializable-object-filter property).
* </p>
* <U>Default</U>: "false"
* </p>
* <U>Since</U>: Geode 1.4
*/
String VALIDATE_SERIALIZABLE_OBJECTS = "validate-serializable-objects";
/**
* The static String definition of the <i>"serializable-object-filter"</i> property
*
* <U>Description</U>A user provided whitelist of objects that the system will allow to serialize.
*
* <p>
* See java.io.ObjectInputFilter.Config for details on the syntax for creating filters.
* https://docs.oracle.com/javase/9/docs/api/java/io/ObjectInputFilter.Config.html
* </p>
* </p>
* <U>Default</U>: "!*"
* </p>
* <U>Since</U>: Geode 1.4
*
*/
String SERIALIZABLE_OBJECT_FILTER = "serializable-object-filter";
}
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,9 @@ public static Class _getAttributeType(String attName) {
m.put(SSL_DEFAULT_ALIAS, "The default certificate alias to be used in a multi-key keystore");
m.put(SSL_WEB_SERVICE_REQUIRE_AUTHENTICATION,
"This property determines is the HTTP service with use mutual ssl authentication.");
m.put(VALIDATE_SERIALIZABLE_OBJECTS,
"If true checks incoming java serializable objects against a filter");
m.put(SERIALIZABLE_OBJECT_FILTER, "The filter to check incoming java serializables against");

dcAttDescriptions = Collections.unmodifiableMap(m);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.distributed.internal;

import java.io.IOException;
import java.util.Collection;

public interface DistributedSystemService {
void init(InternalDistributedSystem internalDistributedSystem);

Class getInterface();

Collection<String> getSerializationWhitelist() throws IOException;
}
Loading

0 comments on commit a2bd578

Please sign in to comment.