src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java
branchhttp-client-branch
changeset 56257 82a9340bdda6
parent 56138 4f92b988600e
child 56410 1b37529eaf3a
--- a/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java	Wed Mar 07 13:00:11 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/ResponseBodyHandlers.java	Wed Mar 07 14:06:39 2018 +0000
@@ -25,13 +25,15 @@
 
 package jdk.internal.net.http;
 
+import java.io.File;
+import java.io.FilePermission;
 import java.io.IOException;
 import java.io.UncheckedIOException;
 import java.net.URI;
+import java.nio.file.Files;
 import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentMap;
@@ -50,21 +52,49 @@
 
     private ResponseBodyHandlers() { }
 
+    private static final String pathForSecurityCheck(Path path) {
+        return path.toFile().getPath();
+    }
+
     /**
      * A Path body handler.
      */
     public static class PathBodyHandler implements BodyHandler<Path>{
         private final Path file;
-        private final List<OpenOption> openOptions;
+        private final List<OpenOption> openOptions;  // immutable list
+        private final FilePermission filePermission;
 
-        public PathBodyHandler(Path file, List<OpenOption> openOptions) {
+        /**
+         * Factory for creating PathBodyHandler.
+         *
+         * Permission checks are performed here before construction of the
+         * PathBodyHandler. Permission checking and construction are
+         * deliberately and tightly co-located.
+         */
+        public static PathBodyHandler create(Path file,
+                                             List<OpenOption> openOptions) {
+            FilePermission filePermission = null;
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                String fn = pathForSecurityCheck(file);
+                FilePermission writePermission = new FilePermission(fn, "write");
+                sm.checkPermission(writePermission);
+                filePermission = writePermission;
+            }
+            return new PathBodyHandler(file, openOptions, filePermission);
+        }
+
+        private PathBodyHandler(Path file,
+                                List<OpenOption> openOptions,
+                                FilePermission filePermission) {
             this.file = file;
             this.openOptions = openOptions;
+            this.filePermission = filePermission;
         }
 
         @Override
         public BodySubscriber<Path> apply(int statusCode, HttpHeaders headers) {
-            return new PathSubscriber(file, openOptions);
+            return new PathSubscriber(file, openOptions, filePermission);
         }
     }
 
@@ -118,10 +148,51 @@
     public static class FileDownloadBodyHandler implements BodyHandler<Path> {
         private final Path directory;
         private final List<OpenOption> openOptions;
+        private final FilePermission[] filePermissions;  // may be null
 
-        public FileDownloadBodyHandler(Path directory, List<OpenOption> openOptions) {
+        /**
+         * Factory for creating FileDownloadBodyHandler.
+         *
+         * Permission checks are performed here before construction of the
+         * FileDownloadBodyHandler. Permission checking and construction are
+         * deliberately and tightly co-located.
+         */
+        public static FileDownloadBodyHandler create(Path directory,
+                                                     List<OpenOption> openOptions) {
+            FilePermission filePermissions[] = null;
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                String fn = pathForSecurityCheck(directory);
+                FilePermission writePermission = new FilePermission(fn, "write");
+                String writePathPerm = fn + File.separatorChar + "*";
+                FilePermission writeInDirPermission = new FilePermission(writePathPerm, "write");
+                sm.checkPermission(writeInDirPermission);
+                FilePermission readPermission = new FilePermission(fn, "read");
+                sm.checkPermission(readPermission);
+
+                // read permission is only needed before determine the below checks
+                // only write permission is required when downloading to the file
+                filePermissions = new FilePermission[] { writePermission, writeInDirPermission };
+            }
+
+            // existence, etc, checks must be after permission checks
+            if (Files.notExists(directory))
+                throw new IllegalArgumentException("non-existent directory: " + directory);
+            if (!Files.isDirectory(directory))
+                throw new IllegalArgumentException("not a directory: " + directory);
+            if (!Files.isWritable(directory))
+                throw new IllegalArgumentException("non-writable directory: " + directory);
+
+            return new FileDownloadBodyHandler(directory, openOptions, filePermissions);
+
+        }
+
+        private FileDownloadBodyHandler(Path directory,
+                                       List<OpenOption> openOptions,
+                                       FilePermission... filePermissions) {
             this.directory = directory;
             this.openOptions = openOptions;
+            this.filePermissions = filePermissions;
         }
 
         /** The "attachment" disposition-type and separator. */
@@ -204,7 +275,7 @@
                         "Resulting file, " + file.toString() + ", outside of given directory");
             }
 
-            return new PathSubscriber(file, openOptions);
+            return new PathSubscriber(file, openOptions, filePermissions);
         }
     }
 }