/* * Copyright (c) 2016, 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. */ package java.io; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.Security; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Function; import jdk.internal.misc.SharedSecrets; /** * Filter classes, array lengths, and graph metrics during deserialization. * *
Warning: Deserialization of untrusted data is inherently dangerous * and should be avoided. Untrusted data should be carefully validated according to the * "Serialization and Deserialization" section of the * {@extLink secure_coding_guidelines_javase Secure Coding Guidelines for Java SE}. * {@extLink serialization_filter_guide Serialization Filtering} describes best * practices for defensive use of serial filters. *
* * If set on an {@link ObjectInputStream}, the {@link #checkInput checkInput(FilterInfo)} * method is called to validate classes, the length of each array, * the number of objects being read from the stream, the depth of the graph, * and the total number of bytes read from the stream. ** A filter can be set via {@link ObjectInputStream#setObjectInputFilter setObjectInputFilter} * for an individual ObjectInputStream. * A filter can be set via {@link Config#setSerialFilter(ObjectInputFilter) Config.setSerialFilter} * to affect every {@code ObjectInputStream} that does not otherwise set a filter. *
* A filter determines whether the arguments are {@link Status#ALLOWED ALLOWED} * or {@link Status#REJECTED REJECTED} and should return the appropriate status. * If the filter cannot determine the status it should return * {@link Status#UNDECIDED UNDECIDED}. * Filters should be designed for the specific use case and expected types. * A filter designed for a particular use may be passed a class that is outside * of the scope of the filter. If the purpose of the filter is to black-list classes * then it can reject a candidate class that matches and report UNDECIDED for others. * A filter may be called with class equals {@code null}, {@code arrayLength} equal -1, * the depth, number of references, and stream size and return a status * that reflects only one or only some of the values. * This allows a filter to specific about the choice it is reporting and * to use other filters without forcing either allowed or rejected status. * *
* Typically, a custom filter should check if a process-wide filter * is configured and defer to it if so. For example, *
{@code * ObjectInputFilter.Status checkInput(FilterInfo info) { * ObjectInputFilter serialFilter = ObjectInputFilter.Config.getSerialFilter(); * if (serialFilter != null) { * ObjectInputFilter.Status status = serialFilter.checkInput(info); * if (status != ObjectInputFilter.Status.UNDECIDED) { * // The process-wide filter overrides this filter * return status; * } * } * if (info.serialClass() != null && * Remote.class.isAssignableFrom(info.serialClass())) { * return Status.REJECTED; // Do not allow Remote objects * } * return Status.UNDECIDED; * } *}*
* Unless otherwise noted, passing a {@code null} argument to a * method in this interface and its nested classes will cause a * {@link NullPointerException} to be thrown. * * @see ObjectInputStream#setObjectInputFilter(ObjectInputFilter) * @since 9 */ @FunctionalInterface public interface ObjectInputFilter { /** * Check the class, array length, number of object references, depth, * stream size, and other available filtering information. * Implementations of this method check the contents of the object graph being created * during deserialization. The filter returns {@link Status#ALLOWED Status.ALLOWED}, * {@link Status#REJECTED Status.REJECTED}, or {@link Status#UNDECIDED Status.UNDECIDED}. * * @param filterInfo provides information about the current object being deserialized, * if any, and the status of the {@link ObjectInputStream} * @return {@link Status#ALLOWED Status.ALLOWED} if accepted, * {@link Status#REJECTED Status.REJECTED} if rejected, * {@link Status#UNDECIDED Status.UNDECIDED} if undecided. */ Status checkInput(FilterInfo filterInfo); /** * FilterInfo provides access to information about the current object * being deserialized and the status of the {@link ObjectInputStream}. * @since 9 */ interface FilterInfo { /** * The class of an object being deserialized. * For arrays, it is the array type. * For example, the array class name of a 2 dimensional array of strings is * "{@code [[Ljava.lang.String;}". * To check the array's element type, iteratively use * {@link Class#getComponentType() Class.getComponentType} while the result * is an array and then check the class. * The {@code serialClass is null} in the case where a new object is not being * created and to give the filter a chance to check the depth, number of * references to existing objects, and the stream size. * * @return class of an object being deserialized; may be null */ Class> serialClass(); /** * The number of array elements when deserializing an array of the class. * * @return the non-negative number of array elements when deserializing * an array of the class, otherwise -1 */ long arrayLength(); /** * The current depth. * The depth starts at {@code 1} and increases for each nested object and * decrements when each nested object returns. * * @return the current depth */ long depth(); /** * The current number of object references. * * @return the non-negative current number of object references */ long references(); /** * The current number of bytes consumed. * @implSpec {@code streamBytes} is implementation specific * and may not be directly related to the object in the stream * that caused the callback. * * @return the non-negative current number of bytes consumed */ long streamBytes(); } /** * The status of a check on the class, array length, number of references, * depth, and stream size. * * @since 9 */ enum Status { /** * The status is undecided, not allowed and not rejected. */ UNDECIDED, /** * The status is allowed. */ ALLOWED, /** * The status is rejected. */ REJECTED; } /** * A utility class to set and get the process-wide filter or create a filter * from a pattern string. If a process-wide filter is set, it will be * used for each {@link ObjectInputStream} that does not set its own filter. *
* When setting the filter, it should be stateless and idempotent, * reporting the same result when passed the same arguments. *
* The filter is configured during the initialization of the {@code ObjectInputFilter.Config}
* class. For example, by calling {@link #getSerialFilter() Config.getSerialFilter}.
* If the system property {@code jdk.serialFilter} is defined, it is used
* to configure the filter.
* If the system property is not defined, and the {@link java.security.Security}
* property {@code jdk.serialFilter} is defined then it is used to configure the filter.
* Otherwise, the filter is not configured during initialization.
* The syntax for each property is the same as for the
* {@link #createFilter(String) createFilter} method.
* If a filter is not configured, it can be set with
* {@link #setSerialFilter(ObjectInputFilter) Config.setSerialFilter}.
*
* @since 9
*/
final class Config {
/* No instances. */
private Config() {}
/**
* Lock object for process-wide filter.
*/
private final static Object serialFilterLock = new Object();
/**
* Debug: Logger
*/
private final static System.Logger configLog;
/**
* Logger for debugging.
*/
static void filterLog(System.Logger.Level level, String msg, Object... args) {
if (configLog != null) {
configLog.log(level, msg, args);
}
}
/**
* The name for the process-wide deserialization filter.
* Used as a system property and a java.security.Security property.
*/
private final static String SERIAL_FILTER_PROPNAME = "jdk.serialFilter";
/**
* The process-wide filter; may be null.
* Lookup the filter in java.security.Security or
* the system property.
*/
private final static ObjectInputFilter configuredFilter;
static {
configuredFilter = AccessController
.doPrivileged((PrivilegedAction
* Patterns are separated by ";" (semicolon). Whitespace is significant and
* is considered part of the pattern.
* If a pattern includes an equals assignment, "{@code =}" it sets a limit.
* If a limit appears more than once the last value is used.
*
* Other patterns match or reject class or package name
* as returned from {@link Class#getName() Class.getName()} and
* if an optional module name is present
* {@link Module#getName() class.getModule().getName()}.
* Note that for arrays the element type is used in the pattern,
* not the array type.
*
* The resulting filter performs the limit checks and then
* tries to match the class, if any. If any of the limits are exceeded,
* the filter returns {@link Status#REJECTED Status.REJECTED}.
* If the class is an array type, the class to be matched is the element type.
* Arrays of any number of dimensions are treated the same as the element type.
* For example, a pattern of "{@code !example.Foo}",
* rejects creation of any instance or array of {@code example.Foo}.
* The first pattern that matches, working from left to right, determines
* the {@link Status#ALLOWED Status.ALLOWED}
* or {@link Status#REJECTED Status.REJECTED} result.
* If the limits are not exceeded and no pattern matches the class,
* the result is {@link Status#UNDECIDED Status.UNDECIDED}.
*
* @param pattern the pattern string to parse; not null
* @return a filter to check a class being deserialized;
* {@code null} if no patterns
* @throws IllegalArgumentException if the pattern string is illegal or
* malformed and cannot be parsed.
* In particular, if any of the following is true:
*
*
*
*
*
*
*/
public static ObjectInputFilter createFilter(String pattern) {
Objects.requireNonNull(pattern, "pattern");
return Global.createFilter(pattern, true);
}
/**
* Returns an ObjectInputFilter from a string of patterns that
* checks only the length for arrays, not the component type.
*
* @param pattern the pattern string to parse; not null
* @return a filter to check a class being deserialized;
* {@code null} if no patterns
*/
static ObjectInputFilter createFilter2(String pattern) {
Objects.requireNonNull(pattern, "pattern");
return Global.createFilter(pattern, false);
}
/**
* Implementation of ObjectInputFilter that performs the checks of
* the process-wide serialization filter. If configured, it will be
* used for all ObjectInputStreams that do not set their own filters.
*
*/
final static class Global implements ObjectInputFilter {
/**
* The pattern used to create the filter.
*/
private final String pattern;
/**
* The list of class filters.
*/
private final List