View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.jcs.io;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.ObjectInputStream;
24  import java.io.ObjectStreamClass;
25  import java.lang.reflect.Proxy;
26  
27  public class ObjectInputStreamClassLoaderAware extends ObjectInputStream {
28      private final ClassLoader classLoader;
29  
30      public ObjectInputStreamClassLoaderAware(final InputStream in, final ClassLoader classLoader) throws IOException {
31          super(in);
32          this.classLoader = classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader();
33      }
34  
35      @Override
36      protected Class<?> resolveClass(final ObjectStreamClass desc) throws ClassNotFoundException {
37          return Class.forName(BlacklistClassResolver.DEFAULT.check(desc.getName()), false, classLoader);
38      }
39  
40      @Override
41      protected Class<?> resolveProxyClass(final String[] interfaces) throws IOException, ClassNotFoundException {
42          final Class<?>[] cinterfaces = new Class[interfaces.length];
43          for (int i = 0; i < interfaces.length; i++) {
44              cinterfaces[i] = Class.forName(interfaces[i], false, classLoader);
45          }
46  
47          try {
48              return Proxy.getProxyClass(classLoader, cinterfaces);
49          } catch (IllegalArgumentException e) {
50              throw new ClassNotFoundException(null, e);
51          }
52      }
53  
54      private static class BlacklistClassResolver {
55          private static final BlacklistClassResolver DEFAULT = new BlacklistClassResolver(
56              toArray(System.getProperty(
57                  "jcs.serialization.class.blacklist",
58                  "org.codehaus.groovy.runtime.,org.apache.commons.collections.functors.,org.apache.xalan")),
59              toArray(System.getProperty("jcs.serialization.class.whitelist")));
60  
61          private final String[] blacklist;
62          private final String[] whitelist;
63  
64          protected BlacklistClassResolver(final String[] blacklist, final String[] whitelist) {
65              this.whitelist = whitelist;
66              this.blacklist = blacklist;
67          }
68  
69          protected boolean isBlacklisted(final String name) {
70              return (whitelist != null && !contains(whitelist, name)) || contains(blacklist, name);
71          }
72  
73          public final String check(final String name) {
74              if (isBlacklisted(name)) {
75                  throw new SecurityException(name + " is not whitelisted as deserialisable, prevented before loading.");
76              }
77              return name;
78          }
79  
80          private static String[] toArray(final String property) {
81              return property == null ? null : property.split(" *, *");
82          }
83  
84          private static boolean contains(final String[] list, String name) {
85              if (list != null) {
86                  for (final String white : list) {
87                      if (name.startsWith(white)) {
88                          return true;
89                      }
90                  }
91              }
92              return false;
93          }
94      }
95  }