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 .io .serialization ;
20+
21+ import java .io .IOException ;
22+ import java .io .InputStream ;
23+ import java .io .InvalidClassException ;
24+ import java .io .ObjectInputStream ;
25+ import java .io .ObjectStreamClass ;
26+ import java .util .ArrayList ;
27+ import java .util .List ;
28+ import java .util .regex .Pattern ;
29+
30+ /**
31+ * <p>
32+ * An <code>ObjectInputStream</code> that's restricted to deserialize
33+ * a limited set of classes.
34+ * </p>
35+ *
36+ * <p>
37+ * Various accept/reject methods allow for specifying which classes
38+ * can be deserialized.
39+ * </p>
40+ *
41+ * <p>
42+ * Design inspired by <a
43+ * href="http://www.ibm.com/developerworks/library/se-lookahead/">IBM
44+ * DeveloperWorks Article</a>.
45+ * </p>
46+ */
47+ public class ValidatingObjectInputStream extends ObjectInputStream {
48+ private final List <ClassNameMatcher > acceptMatchers = new ArrayList <ClassNameMatcher >();
49+ private final List <ClassNameMatcher > rejectMatchers = new ArrayList <ClassNameMatcher >();
50+
51+ /**
52+ * Constructs an object to deserialize the specified input stream.
53+ * At least one accept method needs to be called to specify which
54+ * classes can be deserialized, as by default no classes are
55+ * accepted.
56+ *
57+ * @param input an input stream
58+ * @param acceptor a class acceptor
59+ * @throws IOException if an I/O error occurs while reading stream header
60+ */
61+ public ValidatingObjectInputStream (InputStream input ) throws IOException {
62+ super (input );
63+ }
64+
65+ private void validateClassName (String name ) throws InvalidClassException {
66+ // Reject has precedence over accept
67+ for (ClassNameMatcher m : rejectMatchers ) {
68+ if (m .matches (name )) {
69+ invalidClassNameFound (name );
70+ }
71+ }
72+
73+ boolean ok = false ;
74+ for (ClassNameMatcher m : acceptMatchers ) {
75+ if (m .matches (name )) {
76+ ok = true ;
77+ break ;
78+ }
79+ }
80+ if (!ok ) {
81+ invalidClassNameFound (name );
82+ }
83+ }
84+
85+ /** Called to throw InvalidClassException (by default) if an invalid
86+ * class name is found in deserialization. Can be overridden, for example
87+ * to log those class names.
88+ * By default the name of the invalid class is not included in the
89+ * exception thrown, as that might give too much information from a
90+ * security point of view.
91+ *
92+ * @param className name of the invalid class
93+ * @throws InvalidClassException
94+ */
95+ protected void invalidClassNameFound (String className ) throws InvalidClassException {
96+ throw new InvalidClassException ("Class name not accepted" );
97+ }
98+
99+ @ Override
100+ protected Class <?> resolveClass (ObjectStreamClass osc ) throws IOException , ClassNotFoundException {
101+ validateClassName (osc .getName ());
102+ return super .resolveClass (osc );
103+ }
104+
105+ /** Accept the specified classes for deserialization, unless they
106+ * are otherwise rejected.
107+ * @param classes Classes to accept
108+ * @return this object
109+ */
110+ public ValidatingObjectInputStream accept (Class <?>... classes ) {
111+ for (Class <?> c : classes ) {
112+ acceptMatchers .add (new FullClassNameMatcher (c .getName ()));
113+ }
114+ return this ;
115+ }
116+
117+ /** Reject the specified classes for deserialization, even if they
118+ * are otherwise accepted.
119+ * @param classes Classes to reject
120+ * @return this object
121+ */
122+ public ValidatingObjectInputStream reject (Class <?>... classes ) {
123+ for (Class <?> c : classes ) {
124+ rejectMatchers .add (new FullClassNameMatcher (c .getName ()));
125+ }
126+ return this ;
127+ }
128+
129+ /** Accept the wildcard specified classes for deserialization,
130+ * unless they are otherwise rejected.
131+ * @param patterns Wildcard filename patterns as defined by
132+ * {@link FilenameUtils.wildcardMatch}
133+ * @return this object
134+ */
135+ public ValidatingObjectInputStream accept (String ... patterns ) {
136+ for (String pattern : patterns ) {
137+ acceptMatchers .add (new WildcardClassNameMatcher (pattern ));
138+ }
139+ return this ;
140+ }
141+
142+ /** Reject the wildcard specified classes for deserialization,
143+ * even if they are otherwise accepted.
144+ * @param patterns Wildcard filename patterns as defined by
145+ * {@link FilenameUtils.wildcardMatch}
146+ * @return this object
147+ */
148+ public ValidatingObjectInputStream reject (String ... patterns ) {
149+ for (String pattern : patterns ) {
150+ rejectMatchers .add (new WildcardClassNameMatcher (pattern ));
151+ }
152+ return this ;
153+ }
154+
155+ /** Accept class names that match the supplied pattern for
156+ * deserialization, unless they are otherwise rejected.
157+ * @param pattern standard Java regexp
158+ * @return this object
159+ */
160+ public ValidatingObjectInputStream accept (Pattern pattern ) {
161+ acceptMatchers .add (new RegexpClassNameMatcher (pattern ));
162+ return this ;
163+ }
164+
165+ /** Reject class names that match the supplied pattern for
166+ * deserialization, even if they are otherwise accepted.
167+ * @param pattern standard Java regexp
168+ * @return this object
169+ */
170+ public ValidatingObjectInputStream reject (Pattern pattern ) {
171+ rejectMatchers .add (new RegexpClassNameMatcher (pattern ));
172+ return this ;
173+ }
174+
175+ /** Accept class names where the supplied ClassNameMatcher matches for
176+ * deserialization, unless they are otherwise rejected.
177+ * @param m the matcher to use
178+ * @return this object
179+ */
180+ public ValidatingObjectInputStream accept (ClassNameMatcher m ) {
181+ acceptMatchers .add (m );
182+ return this ;
183+ }
184+
185+ /** Reject class names where the supplied ClassNameMatcher matches for
186+ * deserialization, even if they are otherwise accepted.
187+ * @param m the matcher to use
188+ * @return this object
189+ */
190+ public ValidatingObjectInputStream reject (ClassNameMatcher m ) {
191+ rejectMatchers .add (m );
192+ return this ;
193+ }
194+ }
0 commit comments