/* * This file is part of Bitsquare. * * Bitsquare is free software: you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. * * Bitsquare 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 Affero General Public * License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Bitsquare. If not, see <http://www.gnu.org/licenses/>. */ package io.bitsquare.io; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.util.regex.Pattern; /** * Based on https://github.com/ikkisoft/SerialKiller but we removed the support for a config file as * that introduced many dependencies (like the vulnerable commons collections). */ public class LookAheadObjectInputStream extends ObjectInputStream { private static final Logger log = LoggerFactory.getLogger(LookAheadObjectInputStream.class); // Covers classes used in the objects sent over the P2P network. // We don't add all particular classes as the risk to miss one (specially at updates) is too high and a package // based filter gives us sufficient security. // This white list is not sufficient for the objects used for local persistence! We don't use any white list for those. private static final Pattern[] whiteListP2PNetwork = { Pattern.compile("io\\.bitsquare\\..*$"), Pattern.compile("org\\.bitcoinj\\..*$"), Pattern.compile("java\\.lang\\.Boolean$"), Pattern.compile("java\\.lang\\.Enum$"), Pattern.compile("java\\.lang\\.Integer$"), Pattern.compile("java\\.lang\\.Long$"), Pattern.compile("java\\.lang\\.Double$"), Pattern.compile("java\\.lang\\.Number$"), Pattern.compile("java\\.util\\.ArrayList$"), Pattern.compile("java\\.util\\.Date$"), Pattern.compile("java\\.util\\.HashSet$"), Pattern.compile("java\\.util\\.HashMap$"), // Type Signatures // https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html Pattern.compile("\\[B$") // byte array }; private final static Pattern[] blackList = { Pattern.compile("bsh\\.XThis$"), Pattern.compile("bsh\\.Interpreter$"), Pattern.compile("com\\.mchange\\.v2\\.c3p0\\.impl\\.PoolBackedDataSourceBase$"), Pattern.compile("org\\.apache\\.commons\\.beanutils\\.BeanComparator$"), Pattern.compile("org\\.apache\\.commons\\.collections\\.Transformer$"), Pattern.compile("org\\.apache\\.commons\\.collections\\.functors\\.InvokerTransformer$"), Pattern.compile("org\\.apache\\.commons\\.collections\\.functors\\.ChainedTransformer$"), Pattern.compile("org\\.apache\\.commons\\.collections\\.functors\\.ConstantTransformer$"), Pattern.compile("org\\.apache\\.commons\\.collections\\.functors\\.InstantiateTransformer$"), Pattern.compile("org\\.apache\\.commons\\.collections4\\.functors\\.InvokerTransformer$"), Pattern.compile("org\\.apache\\.commons\\.collections4\\.functors\\.ChainedTransformer$"), Pattern.compile("org\\.apache\\.commons\\.collections4\\.functors\\.ConstantTransformer$"), Pattern.compile("org\\.apache\\.commons\\.collections4\\.functors\\.InstantiateTransformer$"), Pattern.compile("org\\.apache\\.commons\\.collections4\\.comparators\\.TransformingComparator$"), Pattern.compile("org\\.apache\\.commons\\.fileupload\\.disk\\.DiskFileItem$"), Pattern.compile("org\\.apache\\.wicket\\.util\\.upload\\.DiskFileItem$"), Pattern.compile("org\\.codehaus\\.groovy\\.runtime\\.ConvertedClosure$"), Pattern.compile("org\\.codehaus\\.groovy\\.runtime\\.MethodClosure$"), Pattern.compile("org\\.hibernate\\.engine\\.spi\\.TypedValue$"), Pattern.compile("org\\.hibernate\\.tuple\\.component\\.AbstractComponentTuplizer$"), Pattern.compile("org\\.hibernate\\.tuple\\.component\\.PojoComponentTuplizer$"), Pattern.compile("org\\.hibernate\\.type\\.AbstractType$"), Pattern.compile("org\\.hibernate\\.type\\.ComponentType$"), Pattern.compile("org\\.hibernate\\.type\\.Type$"), Pattern.compile("com\\.sun\\.rowset\\.JdbcRowSetImpl$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.builder\\.InterceptionModelBuilder$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.builder\\.MethodReference$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.proxy\\.DefaultInvocationContextFactory$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.proxy\\.InterceptorMethodHandler$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.reader\\.ClassMetadataInterceptorReference$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.reader\\.DefaultMethodMetadata$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.reader\\.ReflectiveClassMetadata$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.reader\\.SimpleInterceptorMetadata$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.spi\\.instance\\.InterceptorInstantiator$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.spi\\.metadata\\.InterceptorReference$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.spi\\.metadata\\.MethodMetadata$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.spi\\.model\\.InterceptionModel$"), Pattern.compile("org\\.jboss\\.(weld\\.)?interceptor\\.spi\\.model\\.InterceptionType$"), Pattern.compile("java\\.rmi\\.registry\\.Registry$"), Pattern.compile("java\\.rmi\\.server\\.ObjID$"), Pattern.compile("java\\.rmi\\.server\\.RemoteObjectInvocationHandler$"), Pattern.compile("net\\.sf\\.json\\.JSONObject$"), Pattern.compile("javax\\.xml\\.transform\\.Templates$"), Pattern.compile("org\\.python\\.core\\.PyObject$"), Pattern.compile("org\\.python\\.core\\.PyBytecode$"), Pattern.compile("org\\.python\\.core\\.PyFunction$"), Pattern.compile("org\\.mozilla\\.javascript\\..*$"), Pattern.compile("org\\.apache\\.myfaces\\.context\\.servlet\\.FacesContextImpl$"), Pattern.compile("org\\.apache\\.myfaces\\.context\\.servlet\\.FacesContextImplBase$"), Pattern.compile("org\\.apache\\.myfaces\\.el\\.CompositeELResolver$"), Pattern.compile("org\\.apache\\.myfaces\\.el\\.unified\\.FacesELContext$"), Pattern.compile("org\\.apache\\.myfaces\\.view\\.facelets\\.el\\.ValueExpressionMethodExpression$"), Pattern.compile("com\\.sun\\.syndication\\.feed\\.impl\\.ObjectBean$"), Pattern.compile("org\\.springframework\\.beans\\.factory\\.ObjectFactory$"), Pattern.compile("org\\.springframework\\.core\\.SerializableTypeWrapper\\$MethodInvokeTypeProvider$"), Pattern.compile("org\\.springframework\\.aop\\.framework\\.AdvisedSupport$"), Pattern.compile("org\\.springframework\\.aop\\.target\\.SingletonTargetSource$"), Pattern.compile("org\\.springframework\\.aop\\.framework\\.JdkDynamicAopProxy$"), Pattern.compile("org\\.springframework\\.core\\.SerializableTypeWrapper\\$TypeProvider$"), Pattern.compile("java\\.util\\.PriorityQueue$"), Pattern.compile("java\\.lang\\.reflect\\.Proxy$"), Pattern.compile("java\\.util\\.Comparator$"), Pattern.compile("org\\.reflections\\.Reflections$"), Pattern.compile("com\\.sun\\.org\\.apache\\.xalan\\.internal\\.xsltc\\.trax\\.TemplatesImpl$"), }; private boolean useWhiteList; /** * @param inputStream The original inputStream * @throws IOException */ public LookAheadObjectInputStream(InputStream inputStream) throws IOException { this(inputStream, true); } /** * @param inputStream The original inputStream * @throws IOException */ public LookAheadObjectInputStream(InputStream inputStream, boolean useWhiteList) throws IOException { super(inputStream); this.useWhiteList = useWhiteList; } @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { String name = desc.getName(); // log.error("resolveClass " + name); for (Pattern pattern : blackList) { if (pattern.matcher(name).find()) { String msg = "We received a blacklisted class at Java deserialization: '" + name + "'" + "(regex pattern: " + pattern.toString() + ")"; log.error(msg); throw new InvalidClassException(msg); } } if (useWhiteList) { boolean whiteListed = false; for (Pattern pattern : whiteListP2PNetwork) { if (pattern.matcher(name).find()) whiteListed = true; } if (!whiteListed) { String msg = "We received a non-whitelisted class at Java deserialization: '" + name + "'"; log.error(msg); throw new InvalidClassException(msg); } } return super.resolveClass(desc); } }