Skip to content

Commit 12cf2c5

Browse files
committed
classes in java.lang and java.util are now whitelisted individually
Signed-off-by: ceki <ceki@qos.ch>
1 parent e9133ed commit 12cf2c5

10 files changed

Lines changed: 120 additions & 65 deletions

File tree

logback-classic/src/main/java/ch/qos/logback/classic/joran/SerializedModelConfigurator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ private void buildModelInterpretationContext(Model topModel) {
107107
private Model retrieveModel(URL url) {
108108
long start = System.currentTimeMillis();
109109
try (InputStream is = url.openStream()) {
110-
HardenedModelInputStream hmis = new HardenedModelInputStream(is);
110+
HardenedModelInputStream hmis = new HardenedModelInputStream(context, is);
111111

112112
Model model = (Model) hmis.readObject();
113113
long diff = System.currentTimeMillis() - start;

logback-classic/src/main/java/ch/qos/logback/classic/joran/serializedModel/HardenedModelInputStream.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package ch.qos.logback.classic.joran.serializedModel;
1616

17+
import ch.qos.logback.core.Context;
1718
import ch.qos.logback.core.model.Model;
1819
import ch.qos.logback.core.net.HardenedObjectInputStream;
1920

@@ -60,7 +61,7 @@ static public List<String> getWhilelist() {
6061

6162
return whitelist;
6263
}
63-
public HardenedModelInputStream(InputStream is) throws IOException {
64-
super(is, getWhilelist());
64+
public HardenedModelInputStream(Context context, InputStream is) throws IOException {
65+
super(context, is, getWhilelist());
6566
}
6667
}

logback-classic/src/main/java/ch/qos/logback/classic/net/SocketNode.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
public class SocketNode implements Runnable {
4343

4444
Socket socket;
45-
LoggerContext context;
45+
LoggerContext loggerContext;
4646
HardenedLoggingEventInputStream hardenedLoggingEventInputStream;
4747
SocketAddress remoteSocketAddress;
4848

@@ -54,7 +54,7 @@ public SocketNode(SimpleSocketServer socketServer, Socket socket, LoggerContext
5454
this.socketServer = socketServer;
5555
this.socket = socket;
5656
remoteSocketAddress = socket.getRemoteSocketAddress();
57-
this.context = context;
57+
this.loggerContext = context;
5858
logger = context.getLogger(SocketNode.class);
5959
}
6060

@@ -67,7 +67,7 @@ public SocketNode(SimpleSocketServer socketServer, Socket socket, LoggerContext
6767
public void run() {
6868

6969
try {
70-
hardenedLoggingEventInputStream = new HardenedLoggingEventInputStream(
70+
hardenedLoggingEventInputStream = new HardenedLoggingEventInputStream(loggerContext,
7171
new BufferedInputStream(socket.getInputStream()));
7272
} catch (Exception e) {
7373
logger.error("Could not open ObjectInputStream to " + socket, e);
@@ -83,7 +83,7 @@ public void run() {
8383
event = (ILoggingEvent) hardenedLoggingEventInputStream.readObject();
8484
// get a logger from the hierarchy. The name of the logger is taken to
8585
// be the name contained in the event.
86-
remoteLogger = context.getLogger(event.getLoggerName());
86+
remoteLogger = loggerContext.getLogger(event.getLoggerName());
8787
// apply the logger-level filter
8888
if (remoteLogger.isEnabledFor(event.getLevel())) {
8989
// finally log the event as if was generated locally

logback-classic/src/main/java/ch/qos/logback/classic/net/server/HardenedLoggingEventInputStream.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.ArrayList;
1919
import java.util.List;
2020

21+
import ch.qos.logback.classic.LoggerContext;
2122
import org.slf4j.helpers.BasicMarker;
2223

2324
import ch.qos.logback.classic.Level;
@@ -58,13 +59,13 @@ static public List<String> getWhilelist() {
5859
return whitelist;
5960
}
6061

61-
public HardenedLoggingEventInputStream(InputStream is) throws IOException {
62-
super(is, getWhilelist());
62+
public HardenedLoggingEventInputStream(LoggerContext loggerContext, InputStream is) throws IOException {
63+
super(loggerContext, is, getWhilelist());
6364
}
6465

65-
public HardenedLoggingEventInputStream(InputStream is, List<String> additionalAuthorizedClasses)
66+
public HardenedLoggingEventInputStream(LoggerContext loggerContext, InputStream is, List<String> additionalAuthorizedClasses)
6667
throws IOException {
67-
this(is);
68+
this(loggerContext, is);
6869
super.addToWhitelist(additionalAuthorizedClasses);
6970
}
7071
}

logback-classic/src/test/java/ch/qos/logback/classic/LoggerSerializationTest.java

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class LoggerSerializationTest {
3939

4040
// force SLF4J initialization for subsequent Logger readResolve operation
4141
org.slf4j.Logger unused = LoggerFactory.getLogger(this.getClass());
42-
LoggerContext lc;
42+
LoggerContext loggerContext;
4343
Logger logger;
4444

4545
ByteArrayOutputStream bos;
@@ -49,9 +49,9 @@ public class LoggerSerializationTest {
4949

5050
@BeforeEach
5151
public void setUp() throws Exception {
52-
lc = new LoggerContext();
53-
lc.setName("testContext");
54-
logger = lc.getLogger(LoggerSerializationTest.class);
52+
loggerContext = new LoggerContext();
53+
loggerContext.setName("testContext");
54+
logger = loggerContext.getLogger(LoggerSerializationTest.class);
5555
// create the byte output stream
5656
bos = new ByteArrayOutputStream();
5757
oos = new ObjectOutputStream(bos);
@@ -60,7 +60,7 @@ public void setUp() throws Exception {
6060

6161
@AfterEach
6262
public void tearDown() throws Exception {
63-
lc = null;
63+
loggerContext = null;
6464
logger = null;
6565
}
6666

@@ -75,32 +75,32 @@ public void basicSerialization() throws IOException, ClassNotFoundException {
7575
@Test
7676
public void deepTreeSerialization() throws IOException {
7777
// crate a tree of loggers under "aaaaaaaa"
78-
Logger a = lc.getLogger("aaaaaaaa");
79-
lc.getLogger("aaaaaaaa.a");
80-
lc.getLogger("aaaaaaaa.a.a");
81-
lc.getLogger("aaaaaaaa.a.b");
82-
lc.getLogger("aaaaaaaa.a.c");
83-
lc.getLogger("aaaaaaaa.a.d");
84-
85-
lc.getLogger("aaaaaaaa.b");
86-
lc.getLogger("aaaaaaaa.b.a");
87-
lc.getLogger("aaaaaaaa.b.b");
88-
lc.getLogger("aaaaaaaa.b.c");
89-
lc.getLogger("aaaaaaaa.b.d");
90-
91-
lc.getLogger("aaaaaaaa.c");
92-
lc.getLogger("aaaaaaaa.c.a");
93-
lc.getLogger("aaaaaaaa.c.b");
94-
lc.getLogger("aaaaaaaa.c.c");
95-
lc.getLogger("aaaaaaaa.c.d");
96-
97-
lc.getLogger("aaaaaaaa.d");
98-
lc.getLogger("aaaaaaaa.d.a");
99-
lc.getLogger("aaaaaaaa.d.b");
100-
lc.getLogger("aaaaaaaa.d.c");
101-
lc.getLogger("aaaaaaaa.d.d");
102-
103-
Logger b = lc.getLogger("b");
78+
Logger a = loggerContext.getLogger("aaaaaaaa");
79+
loggerContext.getLogger("aaaaaaaa.a");
80+
loggerContext.getLogger("aaaaaaaa.a.a");
81+
loggerContext.getLogger("aaaaaaaa.a.b");
82+
loggerContext.getLogger("aaaaaaaa.a.c");
83+
loggerContext.getLogger("aaaaaaaa.a.d");
84+
85+
loggerContext.getLogger("aaaaaaaa.b");
86+
loggerContext.getLogger("aaaaaaaa.b.a");
87+
loggerContext.getLogger("aaaaaaaa.b.b");
88+
loggerContext.getLogger("aaaaaaaa.b.c");
89+
loggerContext.getLogger("aaaaaaaa.b.d");
90+
91+
loggerContext.getLogger("aaaaaaaa.c");
92+
loggerContext.getLogger("aaaaaaaa.c.a");
93+
loggerContext.getLogger("aaaaaaaa.c.b");
94+
loggerContext.getLogger("aaaaaaaa.c.c");
95+
loggerContext.getLogger("aaaaaaaa.c.d");
96+
97+
loggerContext.getLogger("aaaaaaaa.d");
98+
loggerContext.getLogger("aaaaaaaa.d.a");
99+
loggerContext.getLogger("aaaaaaaa.d.b");
100+
loggerContext.getLogger("aaaaaaaa.d.c");
101+
loggerContext.getLogger("aaaaaaaa.d.d");
102+
103+
Logger b = loggerContext.getLogger("b");
104104

105105
writeObject(oos, a);
106106
oos.close();
@@ -122,7 +122,7 @@ public void deepTreeSerialization() throws IOException {
122122
private Foo writeAndRead(Foo foo) throws IOException, ClassNotFoundException {
123123
writeObject(oos, foo);
124124
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
125-
hardenedLoggingEventInputStream = new HardenedLoggingEventInputStream(bis, whitelist);
125+
hardenedLoggingEventInputStream = new HardenedLoggingEventInputStream(loggerContext, bis, whitelist);
126126
Foo fooBack = readFooObject(hardenedLoggingEventInputStream);
127127
hardenedLoggingEventInputStream.close();
128128
return fooBack;
@@ -145,7 +145,7 @@ private void writeObject(ObjectOutputStream oos, Object o) throws IOException {
145145
@Test
146146
public void testCompatibilityWith_v1_0_11() throws IOException, ClassNotFoundException {
147147
FileInputStream fis = new FileInputStream(SERIALIZATION_PREFIX + "logger_v1.0.11.ser");
148-
HardenedObjectInputStream ois = new HardenedLoggingEventInputStream(fis); // new String[]
148+
HardenedObjectInputStream ois = new HardenedLoggingEventInputStream(loggerContext, fis); // new String[]
149149
// {Logger.class.getName(),
150150
// LoggerRemoteView.class.getName()});
151151
Logger a = (Logger) ois.readObject();
@@ -161,7 +161,7 @@ public void testCompatibilityWith_v1_0_11() throws IOException, ClassNotFoundExc
161161
@Test
162162
public void testCompatibilityWith_v1_0_12() throws IOException, ClassNotFoundException {
163163
FileInputStream fis = new FileInputStream(SERIALIZATION_PREFIX + "logger_v1.0.12.ser");
164-
HardenedObjectInputStream ois = new HardenedObjectInputStream(fis, new String[]{Logger.class.getName()});
164+
HardenedObjectInputStream ois = new HardenedObjectInputStream(loggerContext, fis, new String[]{Logger.class.getName()});
165165
Logger a = (Logger) ois.readObject();
166166
ois.close();
167167
assertEquals("a", a.getName());

logback-classic/src/test/java/ch/qos/logback/classic/joran/JoranConfiguratorTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ public void modelSerialization() throws JoranException, IOException, ClassNotFou
844844
StatusPrinter.print(loggerContext);
845845

846846
FileInputStream fis = new FileInputStream(outputPath);
847-
HardenedModelInputStream hmis = new HardenedModelInputStream(fis);
847+
HardenedModelInputStream hmis = new HardenedModelInputStream(loggerContext, fis);
848848

849849
Model model = (Model) hmis.readObject();
850850

logback-classic/src/test/java/ch/qos/logback/classic/joran/serializedModel/ModelSerializationTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package ch.qos.logback.classic.joran.serializedModel;
1616

17+
import ch.qos.logback.classic.LoggerContext;
1718
import ch.qos.logback.classic.model.ConfigurationModel;
1819
import ch.qos.logback.classic.model.LoggerModel;
1920
import ch.qos.logback.core.model.Model;
@@ -35,6 +36,9 @@ public class ModelSerializationTest {
3536
ByteArrayOutputStream bos;
3637
ObjectOutputStream oos;
3738
HardenedObjectInputStream inputStream;
39+
40+
LoggerContext loggerContext = new LoggerContext();
41+
3842
//String[] whitelist = new String[] { };
3943

4044

@@ -66,7 +70,7 @@ public void smoke() throws ClassNotFoundException, IOException {
6670
private Model writeAndRead(Model model) throws IOException, ClassNotFoundException {
6771
writeObject(oos, model);
6872
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
69-
inputStream = new HardenedModelInputStream(bis);
73+
inputStream = new HardenedModelInputStream(loggerContext, bis);
7074
Model fooBack = (Model) inputStream.readObject();
7175
inputStream.close();
7276
return fooBack;

logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventSerializationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ private ILoggingEvent writeAndRead(ILoggingEvent event) throws IOException, Clas
250250
Serializable ser = pst.transform(event);
251251
oos.writeObject(ser);
252252
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
253-
inputStream = new HardenedLoggingEventInputStream(bis);
253+
inputStream = new HardenedLoggingEventInputStream(loggerContext, bis);
254254

255255
return (ILoggingEvent) inputStream.readObject();
256256
}

logback-core/src/main/java/ch/qos/logback/core/net/HardenedObjectInputStream.java

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@
1313
*/
1414
package ch.qos.logback.core.net;
1515

16+
import ch.qos.logback.core.Context;
17+
import ch.qos.logback.core.spi.ContextAwareImpl;
18+
19+
import javax.swing.*;
1620
import java.io.IOException;
1721
import java.io.InputStream;
1822
import java.io.InvalidClassException;
1923
import java.io.ObjectInputFilter;
2024
import java.io.ObjectInputStream;
2125
import java.io.ObjectStreamClass;
2226
import java.util.ArrayList;
27+
import java.util.Arrays;
28+
import java.util.HashMap;
2329
import java.util.List;
2430

2531
/**
@@ -38,32 +44,63 @@
3844
public class HardenedObjectInputStream extends ObjectInputStream {
3945

4046
final private List<String> whitelistedClassNames;
41-
final private static String[] JAVA_PACKAGES = new String[] { "java.lang", "java.util" };
47+
final private static String[] JAVA_CLASSES = new String[] { "java.lang.Boolean",
48+
"java.lang.Byte",
49+
"java.lang.Character",
50+
"java.lang.Double",
51+
"java.lang.Float",
52+
"java.lang.Integer",
53+
"java.lang.Long",
54+
"java.lang.Number",
55+
"java.lang.Short",
56+
"java.lang.String",
57+
"java,lang.Throwable",
58+
"java.util.ArrayList",
59+
"java.util.Collections$EmptyMap",
60+
"java.util.Collections$UnmodifiableMap",
61+
"java.util.concurrent.CopyOnWriteArrayList",
62+
"java.util.HashMap"
63+
//"java.util.HashSet",
64+
//"java.util.Hashtable",
65+
66+
// PASS
67+
//"java.util.LinkedHashMap",
68+
//"java.util.LinkedHashSet",
69+
//"java.util.LinkedList",
70+
//"java.util.Stack",
71+
//"java.util.TreeMap",
72+
//"java.util.TreeSet",
73+
//"java.util.Vector"
74+
};
4275
final private static int DEPTH_LIMIT = 16;
4376
final private static int ARRAY_LIMIT = 10000;
77+
final private static int ERROR_COUNT_LIMIT = 10;
78+
79+
final private ContextAwareImpl contextAware;
80+
final private HashMap<String, Integer> errorMap = new HashMap<>();
4481

45-
public HardenedObjectInputStream(InputStream in, String[] whitelist) throws IOException {
82+
public HardenedObjectInputStream(Context context, InputStream in, String[] whitelistStrings) throws IOException {
83+
this(context, in, Arrays.asList(whitelistStrings));
84+
}
85+
public HardenedObjectInputStream(Context context, InputStream in, List<String> whitelist) throws IOException {
4686
super(in);
87+
88+
if(context != null)
89+
this.contextAware = new ContextAwareImpl(context, this);
90+
else
91+
this.contextAware = null;
92+
4793
this.initObjectFilter();
4894
this.whitelistedClassNames = new ArrayList<String>();
49-
if (whitelist != null) {
50-
for (int i = 0; i < whitelist.length; i++) {
51-
this.whitelistedClassNames.add(whitelist[i]);
52-
}
53-
}
95+
this.whitelistedClassNames.addAll(whitelist);
5496
}
5597

98+
5699
private void initObjectFilter() {
57100
this.setObjectInputFilter(ObjectInputFilter.Config.createFilter(
58101
"maxarray=" + ARRAY_LIMIT + ";maxdepth=" + DEPTH_LIMIT + ";"
59102
));
60103
}
61-
public HardenedObjectInputStream(InputStream in, List<String> whitelist) throws IOException {
62-
super(in);
63-
this.initObjectFilter();
64-
this.whitelistedClassNames = new ArrayList<String>();
65-
this.whitelistedClassNames.addAll(whitelist);
66-
}
67104

68105
@Override
69106
protected Class<?> resolveClass(ObjectStreamClass anObjectStreamClass) throws IOException, ClassNotFoundException {
@@ -78,14 +115,23 @@ protected Class<?> resolveClass(ObjectStreamClass anObjectStreamClass) throws IO
78115
}
79116

80117
private boolean isWhitelisted(String incomingClassName) {
81-
for (int i = 0; i < JAVA_PACKAGES.length; i++) {
82-
if (incomingClassName.startsWith(JAVA_PACKAGES[i]))
118+
for (String javaClass : JAVA_CLASSES) {
119+
if (incomingClassName.equals(javaClass))
83120
return true;
84121
}
85122
for (String whiteListed : whitelistedClassNames) {
86123
if (incomingClassName.equals(whiteListed))
87124
return true;
88125
}
126+
127+
128+
int errorCount = errorMap.getOrDefault(incomingClassName, 0) + 1;
129+
errorMap.put(incomingClassName, errorCount);
130+
if(contextAware != null && errorCount < ERROR_COUNT_LIMIT) {
131+
contextAware.addError("Unauthorized deserialization attempt for class [" + incomingClassName+"]");
132+
contextAware.addError(("If you deem the class to be legitimate, please contact the project maintainers to have it whitelisted."));
133+
}
134+
89135
return false;
90136
}
91137

0 commit comments

Comments
 (0)