001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.io.serialization; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.InvalidClassException; 024import java.io.ObjectInputStream; 025import java.io.ObjectStreamClass; 026import java.util.ArrayList; 027import java.util.List; 028import java.util.regex.Pattern; 029 030/** 031 * An <code>ObjectInputStream</code> that's restricted to deserialize 032 * a limited set of classes. 033 * 034 * <p> 035 * Various accept/reject methods allow for specifying which classes 036 * can be deserialized. 037 * </p> 038 * 039 * <p> 040 * Design inspired by <a 041 * href="http://www.ibm.com/developerworks/library/se-lookahead/">IBM 042 * DeveloperWorks Article</a>. 043 * </p> 044 */ 045public class ValidatingObjectInputStream extends ObjectInputStream { 046 private final List<ClassNameMatcher> acceptMatchers = new ArrayList<ClassNameMatcher>(); 047 private final List<ClassNameMatcher> rejectMatchers = new ArrayList<ClassNameMatcher>(); 048 049 /** 050 * Constructs an object to deserialize the specified input stream. 051 * At least one accept method needs to be called to specify which 052 * classes can be deserialized, as by default no classes are 053 * accepted. 054 * 055 * @param input an input stream 056 * @throws IOException if an I/O error occurs while reading stream header 057 */ 058 public ValidatingObjectInputStream(InputStream input) throws IOException { 059 super(input); 060 } 061 062 /** Check that the classname conforms to requirements. 063 * @param name The class name 064 * @throws InvalidClassException when a non-accepted class is encountered 065 */ 066 private void validateClassName(String name) throws InvalidClassException { 067 // Reject has precedence over accept 068 for (ClassNameMatcher m : rejectMatchers) { 069 if (m.matches(name)) { 070 invalidClassNameFound(name); 071 } 072 } 073 074 boolean ok = false; 075 for (ClassNameMatcher m : acceptMatchers) { 076 if (m.matches(name)) { 077 ok = true; 078 break; 079 } 080 } 081 if (!ok) { 082 invalidClassNameFound(name); 083 } 084 } 085 086 /** 087 * Called to throw <code>InvalidClassException</code> if an invalid 088 * class name is found during deserialization. Can be overridden, for example 089 * to log those class names. 090 * 091 * @param className name of the invalid class 092 * @throws InvalidClassException if the specified class is not allowed 093 */ 094 protected void invalidClassNameFound(String className) throws InvalidClassException { 095 throw new InvalidClassException("Class name not accepted: " + className); 096 } 097 098 @Override 099 protected Class<?> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException { 100 validateClassName(osc.getName()); 101 return super.resolveClass(osc); 102 } 103 104 /** 105 * Accept the specified classes for deserialization, unless they 106 * are otherwise rejected. 107 * 108 * @param classes Classes to accept 109 * @return this object 110 */ 111 public ValidatingObjectInputStream accept(Class<?>... classes) { 112 for (Class<?> c : classes) { 113 acceptMatchers.add(new FullClassNameMatcher(c.getName())); 114 } 115 return this; 116 } 117 118 /** 119 * Reject the specified classes for deserialization, even if they 120 * are otherwise accepted. 121 * 122 * @param classes Classes to reject 123 * @return this object 124 */ 125 public ValidatingObjectInputStream reject(Class<?>... classes) { 126 for (Class<?> c : classes) { 127 rejectMatchers.add(new FullClassNameMatcher(c.getName())); 128 } 129 return this; 130 } 131 132 /** 133 * Accept the wildcard specified classes for deserialization, 134 * unless they are otherwise rejected. 135 * 136 * @param patterns Wildcard filename patterns as defined by 137 * {@link org.apache.commons.io.FilenameUtils#wildcardMatch(String, String) FilenameUtils.wildcardMatch} 138 * @return this object 139 */ 140 public ValidatingObjectInputStream accept(String... patterns) { 141 for (String pattern : patterns) { 142 acceptMatchers.add(new WildcardClassNameMatcher(pattern)); 143 } 144 return this; 145 } 146 147 /** 148 * Reject the wildcard specified classes for deserialization, 149 * even if they are otherwise accepted. 150 * 151 * @param patterns Wildcard filename patterns as defined by 152 * {@link org.apache.commons.io.FilenameUtils#wildcardMatch(String, String) FilenameUtils.wildcardMatch} 153 * @return this object 154 */ 155 public ValidatingObjectInputStream reject(String... patterns) { 156 for (String pattern : patterns) { 157 rejectMatchers.add(new WildcardClassNameMatcher(pattern)); 158 } 159 return this; 160 } 161 162 /** 163 * Accept class names that match the supplied pattern for 164 * deserialization, unless they are otherwise rejected. 165 * 166 * @param pattern standard Java regexp 167 * @return this object 168 */ 169 public ValidatingObjectInputStream accept(Pattern pattern) { 170 acceptMatchers.add(new RegexpClassNameMatcher(pattern)); 171 return this; 172 } 173 174 /** 175 * Reject class names that match the supplied pattern for 176 * deserialization, even if they are otherwise accepted. 177 * 178 * @param pattern standard Java regexp 179 * @return this object 180 */ 181 public ValidatingObjectInputStream reject(Pattern pattern) { 182 rejectMatchers.add(new RegexpClassNameMatcher(pattern)); 183 return this; 184 } 185 186 /** 187 * Accept class names where the supplied ClassNameMatcher matches for 188 * deserialization, unless they are otherwise rejected. 189 * 190 * @param m the matcher to use 191 * @return this object 192 */ 193 public ValidatingObjectInputStream accept(ClassNameMatcher m) { 194 acceptMatchers.add(m); 195 return this; 196 } 197 198 /** 199 * Reject class names where the supplied ClassNameMatcher matches for 200 * deserialization, even if they are otherwise accepted. 201 * 202 * @param m the matcher to use 203 * @return this object 204 */ 205 public ValidatingObjectInputStream reject(ClassNameMatcher m) { 206 rejectMatchers.add(m); 207 return this; 208 } 209}