Package: liblogback-java
Version: 1:1.1.2-1
Severity: important
Tags: upstream patch

Dear Maintainer,

logback versions in wheezy, jessie and stretch are vulnerable to a
deserialization issue.
Logback would try to deserialize data from a socket, but it can't be trusted.
Upstream mitigates this issue by adding a whitelist of allowed classes to be
deserialized.

I've prepared a patch for jessie.

Regards

-- System Information:
Debian Release: 8.7
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'proposed-updates'), (500,
'stable')
Architecture: amd64 (x86_64)
Foreign Architectures: armhf

Kernel: Linux 3.16.0-4-amd64 (SMP w/8 CPU cores)
Locale: LANG=fr_FR.utf8, LC_CTYPE=fr_FR.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)

Versions of packages liblogback-java depends on:
ii  libslf4j-java  1.7.7-1

liblogback-java recommends no packages.

Versions of packages liblogback-java suggests:
ii  glassfish-javaee  1:2.1.1-b31g+dfsg1-2
ii  libjanino-java    2.7.0-2
diff -rPu logback.orig/logback-access/src/main/java/ch/qos/logback/access/net/HardenedAccessEventInputStream.java logback/logback-access/src/main/java/ch/qos/logback/access/net/HardenedAccessEventInputStream.java
--- logback.orig/logback-access/src/main/java/ch/qos/logback/access/net/HardenedAccessEventInputStream.java	1970-01-01 01:00:00.000000000 +0100
+++ logback/logback-access/src/main/java/ch/qos/logback/access/net/HardenedAccessEventInputStream.java	2017-03-04 15:39:00.000000000 +0100
@@ -0,0 +1,16 @@
+package ch.qos.logback.access.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import ch.qos.logback.access.spi.AccessEvent;
+import ch.qos.logback.core.net.HardenedObjectInputStream;
+
+public class HardenedAccessEventInputStream extends HardenedObjectInputStream {
+
+    public HardenedAccessEventInputStream(InputStream in) throws IOException {
+        super(in, new String[] {AccessEvent.class.getName(), String[].class.getName()});
+    }
+
+}
+
diff -rPu logback.orig/logback-access/src/main/java/ch/qos/logback/access/net/SocketNode.java logback/logback-access/src/main/java/ch/qos/logback/access/net/SocketNode.java
--- logback.orig/logback-access/src/main/java/ch/qos/logback/access/net/SocketNode.java	2013-09-07 12:44:46.000000000 +0200
+++ logback/logback-access/src/main/java/ch/qos/logback/access/net/SocketNode.java	2017-03-05 15:09:48.000000000 +0100
@@ -15,7 +15,6 @@
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
-import java.io.ObjectInputStream;
 import java.net.Socket;
 
 import ch.qos.logback.access.spi.AccessContext;
@@ -42,16 +41,15 @@
 
   Socket socket;
   AccessContext context;
-  ObjectInputStream ois;
+  HardenedAccessEventInputStream hardenedOIS;
 
   public SocketNode(Socket socket, AccessContext context) {
     this.socket = socket;
     this.context = context;
     try {
-      ois = new ObjectInputStream(new BufferedInputStream(socket
-          .getInputStream()));
+      hardenedOIS = new HardenedAccessEventInputStream(new BufferedInputStream(socket.getInputStream()));
     } catch (Exception e) {
-      System.out.println("Could not open ObjectInputStream to " + socket + e);
+      System.out.println("Could not open HardenedObjectInputStream to " + socket + e);
     }
   }
 
@@ -61,7 +59,7 @@
     try {
       while (true) {
         // read an event from the wire
-        event = (IAccessEvent) ois.readObject();
+        event = (IAccessEvent) hardenedOIS.readObject();
         //check that the event should be logged
         if (context.getFilterChainDecision(event) == FilterReply.DENY) {
           break;
@@ -81,7 +79,7 @@
     }
 
     try {
-      ois.close();
+      hardenedOIS.close();
     } catch (Exception e) {
       System.out.println("Could not close connection." + e);
     }
diff -rPu logback.orig/logback-classic/src/main/java/ch/qos/logback/classic/net/HardenedLoggingEventInputStream.java logback/logback-classic/src/main/java/ch/qos/logback/classic/net/HardenedLoggingEventInputStream.java
--- logback.orig/logback-classic/src/main/java/ch/qos/logback/classic/net/HardenedLoggingEventInputStream.java	1970-01-01 01:00:00.000000000 +0100
+++ logback/logback-classic/src/main/java/ch/qos/logback/classic/net/HardenedLoggingEventInputStream.java	2017-03-05 15:14:25.000000000 +0100
@@ -0,0 +1,57 @@
+package ch.qos.logback.classic.net.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.helpers.BasicMarker;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ClassPackagingData;
+import ch.qos.logback.classic.spi.IThrowableProxy;
+import ch.qos.logback.classic.spi.LoggerContextVO;
+import ch.qos.logback.classic.spi.LoggerRemoteView;
+import ch.qos.logback.classic.spi.LoggingEventVO;
+import ch.qos.logback.classic.spi.StackTraceElementProxy;
+import ch.qos.logback.classic.spi.ThrowableProxy;
+import ch.qos.logback.classic.spi.ThrowableProxyVO;
+import ch.qos.logback.core.net.HardenedObjectInputStream;
+
+public class HardenedLoggingEventInputStream extends HardenedObjectInputStream {
+
+    static final String ARRAY_PREFIX = "[L";
+    
+    static public List<String> getWhilelist() {
+        List<String> whitelist = new ArrayList<String>();
+        whitelist.add(LoggingEventVO.class.getName());
+        whitelist.add(LoggerContextVO.class.getName());
+        whitelist.add(LoggerRemoteView.class.getName());
+        whitelist.add(ThrowableProxyVO.class.getName());
+        whitelist.add(BasicMarker.class.getName());
+        whitelist.add(Level.class.getName());
+        whitelist.add(Logger.class.getName());
+        whitelist.add(StackTraceElement.class.getName());
+        whitelist.add(StackTraceElement[].class.getName());
+        whitelist.add(ThrowableProxy.class.getName());
+        whitelist.add(ThrowableProxy[].class.getName());
+        whitelist.add(IThrowableProxy.class.getName());
+        whitelist.add(IThrowableProxy[].class.getName());
+        whitelist.add(StackTraceElementProxy.class.getName());
+        whitelist.add(StackTraceElementProxy[].class.getName());
+        whitelist.add(ClassPackagingData.class.getName());
+
+        return whitelist;
+    }
+   
+    public HardenedLoggingEventInputStream(InputStream is) throws IOException {
+        super(is, getWhilelist());
+    }
+    
+    public HardenedLoggingEventInputStream(InputStream is, List<String> additionalAuthorizedClasses) throws IOException {
+        this(is);
+        super.addToWhitelist(additionalAuthorizedClasses);
+    }
+}
+
diff -rPu logback.orig/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderStreamClient.java logback/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderStreamClient.java
--- logback.orig/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderStreamClient.java	2013-09-07 12:44:46.000000000 +0200
+++ logback/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderStreamClient.java	2017-03-07 16:43:38.579569993 +0100
@@ -16,12 +16,12 @@
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.ObjectInputStream;
 import java.net.Socket;
 
 import ch.qos.logback.classic.Logger;
 import ch.qos.logback.classic.LoggerContext;
 import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.net.HardenedObjectInputStream;
 import ch.qos.logback.core.util.CloseUtil;
 
 /**
@@ -86,7 +86,7 @@
    */
   public void run() {
     logger.info(this + ": connected"); 
-    ObjectInputStream ois = null;
+    HardenedObjectInputStream ois = null;
     try {
       ois = createObjectInputStream();
       while (true) {
@@ -124,11 +124,11 @@
     }
   }
 
-  private ObjectInputStream createObjectInputStream() throws IOException {
+  private HardenedObjectInputStream createObjectInputStream() throws IOException {
     if (inputStream != null) {
-      return new ObjectInputStream(inputStream);
+      return new HardenedLoggingEventInputStream(inputStream);
     }
-    return new ObjectInputStream(socket.getInputStream());
+    return new HardenedLoggingEventInputStream(socket.getInputStream());
   }
   
   /**
diff -rPu logback.orig/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketNode.java logback/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketNode.java
--- logback.orig/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketNode.java	2014-01-28 21:05:23.000000000 +0100
+++ logback/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketNode.java	2017-03-05 15:12:34.000000000 +0100
@@ -15,13 +15,13 @@
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
-import java.io.ObjectInputStream;
 import java.net.Socket;
 import java.net.SocketAddress;
 
 import ch.qos.logback.classic.Logger;
 
 import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.net.server.HardenedLoggingEventInputStream;
 import ch.qos.logback.classic.spi.ILoggingEvent;
 
 // Contributors: Moses Hohman <mmhoh...@rainbow.uchicago.edu>
@@ -44,7 +44,7 @@
 
   Socket socket;
   LoggerContext context;
-  ObjectInputStream ois;
+  HardenedLoggingEventInputStream hardenedLoggingEventInputStream;
   SocketAddress remoteSocketAddress;
   
   Logger logger;
@@ -68,8 +68,7 @@
   public void run() {
 
     try {
-      ois = new ObjectInputStream(new BufferedInputStream(socket
-          .getInputStream()));
+      hardenedLoggingEventInputStream = new HardenedLoggingEventInputStream(new BufferedInputStream(socket.getInputStream()));
     } catch (Exception e) {
       logger.error("Could not open ObjectInputStream to " + socket, e);
       closed = true;
@@ -81,7 +80,7 @@
     try {
       while (!closed) {
         // read an event from the wire
-        event = (ILoggingEvent) ois.readObject();
+        event = (ILoggingEvent) hardenedLoggingEventInputStream.readObject();
         // get a logger from the hierarchy. The name of the logger is taken to
         // be the name contained in the event.
         remoteLogger = context.getLogger(event.getLoggerName());
@@ -111,13 +110,13 @@
       return;
     }
     closed = true;
-    if (ois != null) {
+    if (hardenedLoggingEventInputStream != null) {
       try {
-        ois.close();
+        hardenedLoggingEventInputStream.close();
       } catch (IOException e) {
         logger.warn("Could not close connection.", e);
       } finally {
-        ois = null;
+        hardenedLoggingEventInputStream = null;
       }
     }
   }
diff -rPu logback.orig/logback-core/src/main/java/ch/qos/logback/core/net/HardenedObjectInputStream.java logback/logback-core/src/main/java/ch/qos/logback/core/net/HardenedObjectInputStream.java
--- logback.orig/logback-core/src/main/java/ch/qos/logback/core/net/HardenedObjectInputStream.java	1970-01-01 01:00:00.000000000 +0100
+++ logback/logback-core/src/main/java/ch/qos/logback/core/net/HardenedObjectInputStream.java	2017-03-07 15:49:29.360186454 +0100
@@ -0,0 +1,64 @@
+package ch.qos.logback.core.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidClassException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 
+ * @author Ceki G&uuml;lc&uuml;
+ * @since 1.2.0
+ */
+public class HardenedObjectInputStream extends ObjectInputStream {
+
+    final List<String> whitelistedClassNames;
+    final static String[] JAVA_PACKAGES = new String[] { "java.lang", "java.util" };
+
+    public HardenedObjectInputStream(InputStream in, String[] whilelist) throws IOException {
+        super(in);
+        this.whitelistedClassNames = new ArrayList<String>();
+        if (whilelist != null) {
+            for (int i = 0; i < whilelist.length; i++) {
+                this.whitelistedClassNames.add(whilelist[i]);
+            }
+        }
+    }
+
+    public HardenedObjectInputStream(InputStream in, List<String> whitelist) throws IOException {
+        super(in);
+
+        this.whitelistedClassNames = new ArrayList<String>();
+        this.whitelistedClassNames.addAll(whitelist);
+    }
+
+    @Override
+    protected Class<?> resolveClass(ObjectStreamClass anObjectStreamClass) throws IOException, ClassNotFoundException {
+        String incomingClassName = anObjectStreamClass.getName();
+        if(!isWhitelisted(incomingClassName)) {
+            throw new InvalidClassException("Unauthorized deserialization attempt", anObjectStreamClass.getName());
+        }
+    
+        return super.resolveClass(anObjectStreamClass);
+    }
+
+    private boolean isWhitelisted(String incomingClassName) {
+        for(int i = 0; i < JAVA_PACKAGES.length; i++) {
+            if(incomingClassName.startsWith(JAVA_PACKAGES[i]))
+                return true;
+        }
+        for(String whiteListed: whitelistedClassNames) {
+            if(incomingClassName.equals(whiteListed))
+                return true;
+        }
+        return false;
+    }
+
+    protected void addToWhitelist(List<String> additionalAuthorizedClasses) {
+        whitelistedClassNames.addAll(additionalAuthorizedClasses);
+    }
+}
+
__
This is the maintainer address of Debian's Java team
<http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-maintainers>. 
Please use
debian-j...@lists.debian.org for discussions and questions.

Reply via email to