CVE-2021-21290 (published 2021-02-08T20:15Z)

Netty is an open-source, asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. In Netty before version 4.1.59.Final there is a vulnerability on Unix-like systems involving an insecure temp file. When netty\'s multipart decoders are used local information disclosure can occur via the local system temporary directory if temporary storing uploads on the disk is enabled. On unix-like systems, the temporary directory is shared between all user. As such, writing to this directory using APIs that do not explicitly set the file/directory permissions can lead to information disclosure. Of note, this does not impact modern MacOS Operating Systems. The method "File.createTempFile" on unix-like systems creates a random file, but, by default will create this file with the permissions "-rw-r--r--". Thus, if sensitive information is written to this file, other local users can read this information. This is the case in netty\'s "AbstractDiskHttpData" is vulnerable. This has been fixed in version 4.1.59.Final. As a workaround, one may specify your own "java.io.tmpdir" when you start the JVM or use "DefaultHttpDataFactory.setBaseDir(...)" to set the directory to something that is only readable by the current user.

Severity: LOW
CVSS v2 base score: 1.9 (AV:L/AC:M/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 5.5, MEDIUM (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-378
cwe_name: Creation of Temporary File With Insecure Permissions
description: Opening temporary files without appropriate measures or controls can leave the file, its contents and any function that it impacts vulnerable to attack.
extended_description: 
            
         
         
cwe_id: CWE-379
cwe_name: Creation of Temporary File in Directory with Insecure Permissions
description: The product creates a temporary file in a directory whose permissions allow unintended actors to determine the file's existence or otherwise access that file.
extended_description: On some operating systems, the fact that the temporary file exists may be apparent to any user with sufficient privileges to access that directory. Since the file is visible, the application that is using the temporary file could be known. If one has access to list the processes on the system, the attacker has gained information about what the user is doing at that time. By correlating this with the applications the user is running, an attacker could potentially discover what a user's actions are. From this, higher levels of security could be breached.
         

Fix information:
repo_url: https://github.com/netty/netty
commit_details: repo_url: https://github.com/netty/netty
author: Norman Maurer
author_date: 2021-02-08 11:44:05+01:00
msg: Use Files.createTempFile(...) to ensure the file is created with proper permissions

Motivation:

File.createTempFile(String, String)` will create a temporary file in the system temporary directory if the 'java.io.tmpdir'. The permissions on that file utilize the umask. In a majority of cases, this means that the file that java creates has the permissions: `-rw-r--r--`, thus, any other local user on that system can read the contents of that file.
This can be a security concern if any sensitive data is stored in this file.

This was reported by Jonathan Leitschuh <jonathan.leitschuh@gmail.com> as a security problem.

Modifications:

Use Files.createTempFile(...) which will use safe-defaults when running on java 7 and later. If running on java 6 there isnt much we can do, which is fair enough as java 6 shouldnt be considered "safe" anyway.

Result:

Create temporary files with sane permissions by default.
repository: repo_url: https://github.com/netty/netty
repo_name: netty/netty
description: Netty project - an event-driven asynchronous network application framework
repo_language: Java
owner: netty
file_changes: filename: AbstractByteBufTest.java
change_type: ModificationType.MODIFY
diff: @@ -4551,7 +4551,7 @@ public abstract class AbstractByteBufTest {
 
     @Test
     public void testReadBytesAndWriteBytesWithFileChannel() throws IOException {
-        File file = File.createTempFile("file-channel", ".tmp");
+        File file = PlatformDependent.createTempFile("file-channel", ".tmp", null);
         RandomAccessFile randomAccessFile = null;
         try {
             randomAccessFile = new RandomAccessFile(file, "rw");
@@ -4594,7 +4594,7 @@ public abstract class AbstractByteBufTest {
 
     @Test
     public void testGetBytesAndSetBytesWithFileChannel() throws IOException {
-        File file = File.createTempFile("file-channel", ".tmp");
+        File file = PlatformDependent.createTempFile("file-channel", ".tmp", null);
         RandomAccessFile randomAccessFile = null;
         try {
             randomAccessFile = new RandomAccessFile(file, "rw");

programming_language: Java
filename: ReadOnlyDirectByteBufferBufTest.java
change_type: ModificationType.MODIFY
diff: @@ -306,7 +306,7 @@ public class ReadOnlyDirectByteBufferBufTest {
 
     @Test
     public void testWrapMemoryMapped() throws Exception {
-        File file = File.createTempFile("netty-test", "tmp");
+        File file = PlatformDependent.createTempFile("netty-test", "tmp", null);
         FileChannel output = null;
         FileChannel input = null;
         ByteBuf b1 = null;

programming_language: Java
filename: AbstractDiskHttpData.java
change_type: ModificationType.MODIFY
diff: @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
 import io.netty.handler.codec.http.HttpConstants;
 import io.netty.util.internal.EmptyArrays;
 import io.netty.util.internal.ObjectUtil;
+import io.netty.util.internal.PlatformDependent;
 import io.netty.util.internal.logging.InternalLogger;
 import io.netty.util.internal.logging.InternalLoggerFactory;
 
@@ -88,9 +89,9 @@ public abstract class AbstractDiskHttpData extends AbstractHttpData {
         File tmpFile;
         if (getBaseDirectory() == null) {
             // create a temporary file
-            tmpFile = File.createTempFile(getPrefix(), newpostfix);
+            tmpFile = PlatformDependent.createTempFile(getPrefix(), newpostfix, null);
         } else {
-            tmpFile = File.createTempFile(getPrefix(), newpostfix, new File(
+            tmpFile = PlatformDependent.createTempFile(getPrefix(), newpostfix, new File(
                     getBaseDirectory()));
         }
         if (deleteOnExit()) {

programming_language: Java
filename: HttpChunkedInputTest.java
change_type: ModificationType.MODIFY
diff: @@ -25,6 +25,7 @@ import io.netty.handler.stream.ChunkedNioFile;
 import io.netty.handler.stream.ChunkedNioStream;
 import io.netty.handler.stream.ChunkedStream;
 import io.netty.handler.stream.ChunkedWriteHandler;
+import io.netty.util.internal.PlatformDependent;
 import org.junit.Test;
 
 import java.io.ByteArrayInputStream;
@@ -46,7 +47,7 @@ public class HttpChunkedInputTest {
 
         FileOutputStream out = null;
         try {
-            TMP = File.createTempFile("netty-chunk-", ".tmp");
+            TMP = PlatformDependent.createTempFile("netty-chunk-", ".tmp", null);
             TMP.deleteOnExit();
             out = new FileOutputStream(TMP);
             out.write(BYTES);

programming_language: Java
filename: AbstractDiskHttpDataTest.java
change_type: ModificationType.MODIFY
diff: @@ -39,7 +39,7 @@ public class AbstractDiskHttpDataTest {
     public void testGetChunk() throws Exception {
         TestHttpData test = new TestHttpData("test", UTF_8, 0);
         try {
-            File tmpFile = File.createTempFile(UUID.randomUUID().toString(), ".tmp");
+            File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null);
             tmpFile.deleteOnExit();
             FileOutputStream fos = new FileOutputStream(tmpFile);
             byte[] bytes = new byte[4096];

programming_language: Java
filename: AbstractMemoryHttpDataTest.java
change_type: ModificationType.MODIFY
diff: @@ -43,7 +43,7 @@ public class AbstractMemoryHttpDataTest {
     public void testSetContentFromFile() throws Exception {
         TestHttpData test = new TestHttpData("test", UTF_8, 0);
         try {
-            File tmpFile = File.createTempFile(UUID.randomUUID().toString(), ".tmp");
+            File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null);
             tmpFile.deleteOnExit();
             FileOutputStream fos = new FileOutputStream(tmpFile);
             byte[] bytes = new byte[4096];
@@ -70,7 +70,7 @@ public class AbstractMemoryHttpDataTest {
     public void testRenameTo() throws Exception {
         TestHttpData test = new TestHttpData("test", UTF_8, 0);
         try {
-            File tmpFile = File.createTempFile(UUID.randomUUID().toString(), ".tmp");
+            File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null);
             tmpFile.deleteOnExit();
             final int totalByteCount = 4096;
             byte[] bytes = new byte[totalByteCount];

programming_language: Java
filename: DiskFileUploadTest.java
change_type: ModificationType.MODIFY
diff: @@ -273,7 +273,7 @@ public class DiskFileUploadTest {
             assertEquals(maxSize, f1.length());
             byte[] bytes = new byte[8];
             PlatformDependent.threadLocalRandom().nextBytes(bytes);
-            File tmpFile = File.createTempFile(UUID.randomUUID().toString(), ".tmp");
+            File tmpFile = PlatformDependent.createTempFile(UUID.randomUUID().toString(), ".tmp", null);
             tmpFile.deleteOnExit();
             FileOutputStream fos = new FileOutputStream(tmpFile);
             try {

programming_language: Java
filename: NativeLibraryLoader.java
change_type: ModificationType.MODIFY
diff: @@ -177,7 +177,7 @@ public final class NativeLibraryLoader {
             String prefix = libname.substring(0, index);
             String suffix = libname.substring(index);
 
-            tmpFile = File.createTempFile(prefix, suffix, WORKDIR);
+            tmpFile = PlatformDependent.createTempFile(prefix, suffix, WORKDIR);
             in = url.openStream();
             out = new FileOutputStream(tmpFile);
 

programming_language: Java
filename: PlatformDependent.java
change_type: ModificationType.MODIFY
diff: @@ -38,6 +38,7 @@ import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.nio.file.Files;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Arrays;
@@ -1389,6 +1390,24 @@ public final class PlatformDependent {
         return LINUX_OS_CLASSIFIERS;
     }
 
+    @SuppressJava6Requirement(reason = "Guarded by version check")
+    public static File createTempFile(String prefix, String suffix, File directory) throws IOException {
+        if (javaVersion() >= 7) {
+            if (directory == null) {
+                return Files.createTempFile(prefix, suffix).toFile();
+            }
+            return Files.createTempFile(directory.toPath(), prefix, suffix).toFile();
+        }
+        if (directory == null) {
+            return File.createTempFile(prefix, suffix);
+        }
+        File file = File.createTempFile(prefix, suffix, directory);
+        // Try to adjust the perms, if this fails there is not much else we can do...
+        file.setReadable(false, false);
+        file.setReadable(true, true);
+        return file;
+    }
+
     /**
      * Adds only those classifier strings to <tt>dest</tt> which are present in <tt>allowed</tt>.
      *

programming_language: Java
filename: SelfSignedCertificate.java
change_type: ModificationType.MODIFY
diff: @@ -20,6 +20,7 @@ import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.handler.codec.base64.Base64;
 import io.netty.util.CharsetUtil;
+import io.netty.util.internal.PlatformDependent;
 import io.netty.util.internal.SystemPropertyUtil;
 import io.netty.util.internal.ThrowableUtil;
 import io.netty.util.internal.logging.InternalLogger;
@@ -30,6 +31,7 @@ import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.nio.file.Files;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.NoSuchAlgorithmException;
@@ -330,7 +332,7 @@ public final class SelfSignedCertificate {
             wrappedBuf.release();
         }
 
-        File keyFile = File.createTempFile("keyutil_" + fqdn + '_', ".key");
+        File keyFile = PlatformDependent.createTempFile("keyutil_" + fqdn + '_', ".key", null);
         keyFile.deleteOnExit();
 
         OutputStream keyOut = new FileOutputStream(keyFile);
@@ -361,7 +363,7 @@ public final class SelfSignedCertificate {
             wrappedBuf.release();
         }
 
-        File certFile = File.createTempFile("keyutil_" + fqdn + '_', ".crt");
+        File certFile = PlatformDependent.createTempFile("keyutil_" + fqdn + '_', ".crt", null);
         certFile.deleteOnExit();
 
         OutputStream certOut = new FileOutputStream(certFile);

programming_language: Java
filename: ChunkedWriteHandlerTest.java
change_type: ModificationType.MODIFY
diff: @@ -26,6 +26,7 @@ import io.netty.channel.ChannelPromise;
 import io.netty.channel.embedded.EmbeddedChannel;
 import io.netty.util.CharsetUtil;
 import io.netty.util.ReferenceCountUtil;
+import io.netty.util.internal.PlatformDependent;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -55,7 +56,7 @@ public class ChunkedWriteHandlerTest {
 
         FileOutputStream out = null;
         try {
-            TMP = File.createTempFile("netty-chunk-", ".tmp");
+            TMP = PlatformDependent.createTempFile("netty-chunk-", ".tmp", null);
             TMP.deleteOnExit();
             out = new FileOutputStream(TMP);
             out.write(BYTES);

programming_language: Java
filename: FileRegionThrottleTest.java
change_type: ModificationType.MODIFY
diff: @@ -32,6 +32,7 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
 import io.netty.channel.socket.nio.NioSocketChannel;
 import io.netty.handler.codec.LineBasedFrameDecoder;
 import io.netty.util.CharsetUtil;
+import io.netty.util.internal.PlatformDependent;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -61,7 +62,7 @@ public class FileRegionThrottleTest {
             BYTES[i] = (byte) r.nextInt(255);
         }
 
-        tmp = File.createTempFile("netty-traffic", ".tmp");
+        tmp = PlatformDependent.createTempFile("netty-traffic", ".tmp", null);
         tmp.deleteOnExit();
         FileOutputStream out = null;
         try {

programming_language: Java
filename: SocketFileRegionTest.java
change_type: ModificationType.MODIFY
diff: @@ -102,7 +102,7 @@ public class SocketFileRegionTest extends AbstractSocketTest {
     }
 
     public void testFileRegionCountLargerThenFile(ServerBootstrap sb, Bootstrap cb) throws Throwable {
-        File file = File.createTempFile("netty-", ".tmp");
+        File file = PlatformDependent.createTempFile("netty-", ".tmp", null);
         file.deleteOnExit();
 
         final FileOutputStream out = new FileOutputStream(file);
@@ -136,7 +136,7 @@ public class SocketFileRegionTest extends AbstractSocketTest {
         cb.option(ChannelOption.AUTO_READ, autoRead);
 
         final int bufferSize = 1024;
-        final File file = File.createTempFile("netty-", ".tmp");
+        final File file = PlatformDependent.createTempFile("netty-", ".tmp", null);
         file.deleteOnExit();
 
         final FileOutputStream out = new FileOutputStream(file);

programming_language: Java
filename: EpollSpliceTest.java
change_type: ModificationType.MODIFY
diff: @@ -28,6 +28,7 @@ import io.netty.channel.EventLoopGroup;
 import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.channel.unix.FileDescriptor;
 import io.netty.util.NetUtil;
+import io.netty.util.internal.PlatformDependent;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -192,7 +193,7 @@ public class EpollSpliceTest {
     @Test(timeout = 10000)
     public void spliceToFile() throws Throwable {
         EventLoopGroup group = new EpollEventLoopGroup(1);
-        File file = File.createTempFile("netty-splice", null);
+        File file = PlatformDependent.createTempFile("netty-splice", null, null);
         file.deleteOnExit();
 
         SpliceHandler sh = new SpliceHandler(file);

programming_language: Java
filename: UnixTestUtils.java
change_type: ModificationType.MODIFY
diff: @@ -17,6 +17,7 @@ package io.netty.channel.unix.tests;
 
 import io.netty.channel.unix.DomainSocketAddress;
 import io.netty.channel.unix.Socket;
+import io.netty.util.internal.PlatformDependent;
 
 import java.io.File;
 import java.io.IOException;
@@ -26,7 +27,7 @@ public final class UnixTestUtils {
         try {
             File file;
             do {
-                file = File.createTempFile("NETTY", "UDS");
+                file = PlatformDependent.createTempFile("NETTY", "UDS", null);
                 if (!file.delete()) {
                     throw new IOException("failed to delete: " + file);
                 }

programming_language: Java
filename: DefaultFileRegionTest.java
change_type: ModificationType.MODIFY
diff: @@ -39,7 +39,7 @@ public class DefaultFileRegionTest {
     }
 
     private static File newFile() throws IOException {
-        File file = File.createTempFile("netty-", ".tmp");
+        File file = PlatformDependent.createTempFile("netty-", ".tmp", null);
         file.deleteOnExit();
 
         final FileOutputStream out = new FileOutputStream(file);

programming_language: Java