# HG changeset patch # User egahlin # Date 1565661509 -7200 # Node ID 4ce66d271065a7878e35b3583439217a3e5475ab # Parent c75c241c492ab940b27e0e8f4bf1f45290ff8784 Security handling, but two tests fails with driver diff -r c75c241c492a -r 4ce66d271065 src/jdk.jfr/share/classes/jdk/jfr/consumer/EventDirectoryStream.java --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventDirectoryStream.java Fri Aug 09 19:16:55 2019 +0200 +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventDirectoryStream.java Tue Aug 13 03:58:29 2019 +0200 @@ -35,7 +35,6 @@ import java.util.Objects; import java.util.function.Consumer; -import jdk.jfr.internal.SecuritySupport.SafePath; import jdk.jfr.internal.Utils; import jdk.jfr.internal.consumer.FileAccess; import jdk.jfr.internal.consumer.RecordingInput; @@ -64,7 +63,7 @@ super(acc, active); this.fileAccess = fileAccess; this.active = active; - repositoryFiles = new RepositoryFiles(fileAccess, p == null ? null : new SafePath(p)); + repositoryFiles = new RepositoryFiles(fileAccess, p ); } @Override diff -r c75c241c492a -r 4ce66d271065 src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java Fri Aug 09 19:16:55 2019 +0200 +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java Tue Aug 13 03:58:29 2019 +0200 @@ -34,6 +34,7 @@ import java.util.Objects; import java.util.function.Consumer; +import jdk.jfr.internal.SecuritySupport; import jdk.jfr.internal.Utils; import jdk.jfr.internal.consumer.FileAccess; @@ -60,7 +61,7 @@ */ public static EventStream openRepository() throws IOException { Utils.checkAccessFlightRecorder(); - return new EventDirectoryStream(AccessController.getContext(), null, FileAccess.PRIVILIGED, false); + return new EventDirectoryStream(AccessController.getContext(), null, SecuritySupport.PRIVILIGED, false); } /** diff -r c75c241c492a -r 4ce66d271065 src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java --- a/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java Fri Aug 09 19:16:55 2019 +0200 +++ b/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingStream.java Tue Aug 13 03:58:29 2019 +0200 @@ -40,8 +40,8 @@ import jdk.jfr.Recording; import jdk.jfr.internal.PlatformRecording; import jdk.jfr.internal.PrivateAccess; +import jdk.jfr.internal.SecuritySupport; import jdk.jfr.internal.Utils; -import jdk.jfr.internal.consumer.FileAccess; /** * A recording stream produces events from the current JVM (Java Virtual @@ -100,7 +100,7 @@ this.recording = new Recording(); this.recording.setFlushInterval(Duration.ofMillis(1000)); try { - this.stream = new EventDirectoryStream(acc, null, FileAccess.PRIVILIGED, true); + this.stream = new EventDirectoryStream(acc, null, SecuritySupport.PRIVILIGED, true); } catch (IOException ioe) { throw new IllegalStateException(ioe.getMessage()); } diff -r c75c241c492a -r 4ce66d271065 src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java Fri Aug 09 19:16:55 2019 +0200 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/SecuritySupport.java Tue Aug 13 03:58:29 2019 +0200 @@ -38,6 +38,7 @@ import java.lang.reflect.ReflectPermission; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; +import java.nio.file.DirectoryStream; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -65,6 +66,7 @@ import jdk.jfr.FlightRecorderListener; import jdk.jfr.FlightRecorderPermission; import jdk.jfr.Recording; +import jdk.jfr.internal.consumer.FileAccess; /** * Contains JFR code that does @@ -75,7 +77,7 @@ private final static MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); private final static Module JFR_MODULE = Event.class.getModule(); public final static SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr"); - + public final static FileAccess PRIVILIGED = new Privileged(); static final SafePath USER_HOME = getPathInProperty("user.home", null); static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null); @@ -150,7 +152,7 @@ * a malicious provider. * */ - public static final class SafePath { + public static final class SafePath implements Comparable { private final Path path; private final String text; @@ -168,9 +170,18 @@ return path; } + public File toFile() { + return path.toFile(); + } + public String toString() { return text; } + + @Override + public int compareTo(SafePath that) { + return that.text.compareTo(this.text); + } } private interface RunnableWithCheckedException { @@ -436,4 +447,32 @@ public static SafePath getAbsolutePath(SafePath path) throws IOException { return new SafePath(doPrivilegedIOWithReturn((()-> path.toPath().toAbsolutePath()))); } + + private final static class Privileged extends FileAccess { + @Override + public RandomAccessFile openRAF(File f, String mode) throws IOException { + return doPrivilegedIOWithReturn( () -> new RandomAccessFile(f, mode)); + } + + @Override + public DirectoryStream newDirectoryStream(Path directory) throws IOException { + return doPrivilegedIOWithReturn( () -> Files.newDirectoryStream(directory)); + } + + @Override + public String getAbsolutePath(File f) throws IOException { + return doPrivilegedIOWithReturn( () ->f.getAbsolutePath()); + } + @Override + public long length(File f) throws IOException { + return doPrivilegedIOWithReturn( () ->f.length()); + } + + @Override + public long fileSize(Path p) throws IOException { + return doPrivilegedIOWithReturn( () ->Files.size(p)); + } + } + + } diff -r c75c241c492a -r 4ce66d271065 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/FileAccess.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/FileAccess.java Fri Aug 09 19:16:55 2019 +0200 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/FileAccess.java Tue Aug 13 03:58:29 2019 +0200 @@ -1,7 +1,6 @@ package jdk.jfr.internal.consumer; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.file.DirectoryStream; @@ -10,37 +9,40 @@ // Protected by modular boundaries. public abstract class FileAccess { - public final static FileAccess PRIVILIGED = new UnPriviliged(); - // TODO: Should be changed Priviliged class public final static FileAccess UNPRIVILIGED = new UnPriviliged(); - abstract RandomAccessFile openRAF(File f, String mode) throws FileNotFoundException; - abstract DirectoryStream newDirectoryStream(Path repository) throws IOException; + public abstract RandomAccessFile openRAF(File f, String mode) throws IOException; - static class Priviliged extends FileAccess { - @Override - RandomAccessFile openRAF(File f, String mode) { - // TDOO: Implement - return null; - } + public abstract DirectoryStream newDirectoryStream(Path repository) throws IOException; + + public abstract String getAbsolutePath(File f) throws IOException; + public abstract long length(File f) throws IOException; + + public abstract long fileSize(Path p) throws IOException; + + private static class UnPriviliged extends FileAccess { @Override - protected DirectoryStream newDirectoryStream(Path repository) { - // TDOO: Implement - return null; - } - } - - static class UnPriviliged extends FileAccess { - @Override - RandomAccessFile openRAF(File f, String mode) throws FileNotFoundException { + public RandomAccessFile openRAF(File f, String mode) throws IOException { return new RandomAccessFile(f, mode); } @Override - DirectoryStream newDirectoryStream(Path dir) throws IOException { + public DirectoryStream newDirectoryStream(Path dir) throws IOException { return Files.newDirectoryStream(dir); } + public String getAbsolutePath(File f) throws IOException { + return f.getAbsolutePath(); + } + + public long length(File f) throws IOException { + return f.length(); + } + + @Override + public long fileSize(Path p) throws IOException { + return Files.size(p); + } } } diff -r c75c241c492a -r 4ce66d271065 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java Fri Aug 09 19:16:55 2019 +0200 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java Tue Aug 13 03:58:29 2019 +0200 @@ -81,13 +81,13 @@ } private void initialize(File f) throws IOException { - this.filename = f.getAbsolutePath().toString(); + this.filename = fileAccess.getAbsolutePath(f); this.file = fileAccess.openRAF(f, "r"); this.position = 0; this.size = -1; this.currentBlock.reset(); - this.previousBlock.reset(); - if (f.length() < 8) { + previousBlock.reset(); + if (fileAccess.length(f) < 8) { throw new IOException("Not a valid Flight Recorder file. File length is only " + f.length() + " bytes."); } } diff -r c75c241c492a -r 4ce66d271065 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java Fri Aug 09 19:16:55 2019 +0200 +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RepositoryFiles.java Tue Aug 13 03:58:29 2019 +0200 @@ -26,7 +26,6 @@ import java.io.IOException; import java.nio.file.DirectoryStream; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; @@ -52,8 +51,8 @@ private volatile boolean closed; private Path repository; - public RepositoryFiles(FileAccess fileAccess, SafePath repository) { - this.repository = repository == null ? null : repository.toPath(); + public RepositoryFiles(FileAccess fileAccess, Path repository) { + this.repository = repository; this.fileAccess = fileAccess; } @@ -62,66 +61,71 @@ } public Path lastPath() { - // Wait for chunks + if (waitForPaths()) { + return pathSet.lastEntry().getValue(); + } + return null; // closed + } + + public Path firstPath(long startTimeNanos) { + if (waitForPaths()) { + // Pick closest chunk before timestamp + Long time = pathSet.floorKey(startTimeNanos); + if (time != null) { + startTimeNanos = time; + } + return path(startTimeNanos); + } + return null; // closed + } + + private boolean waitForPaths() { while (!closed) { try { if (updatePaths()) { break; } } catch (IOException e) { - // ignore, not yet available + Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "IOException during repository file scan " + e.getMessage()); + // This can happen if a chunk is being removed + // between the file was discovered and an instance + // was accessed, or if new file has been written yet + // Just ignore, and retry later. } - } - if (closed) { - return null; + nap(); } - // Pick the last - return pathSet.lastEntry().getValue(); - } - - public Path firstPath(long startTimeNanos) { - return path(startTimeNanos, true); + return !closed; } public Path nextPath(long startTimeNanos) { - return path(startTimeNanos, false); + return path(startTimeNanos); } - private Path path(long timestamp, boolean first) { - while (!closed) { - Long time = timestamp; - if (first) { - // Pick closest chunk before timestamp - time = pathSet.floorKey(timestamp); - } - if (time != null) { - SortedMap after = pathSet.tailMap(time); - if (!after.isEmpty()) { - Path path = after.get(after.firstKey()); - Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.TRACE, "Return path " + path + " for start time nanos " + timestamp); - return path; - } + private Path path(long timestamp) { + if (closed) { + return null; + } + while (true) { + SortedMap after = pathSet.tailMap(timestamp); + if (!after.isEmpty()) { + Path path = after.get(after.firstKey()); + Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.TRACE, "Return path " + path + " for start time nanos " + timestamp); + return path; } - try { - if (updatePaths()) { - continue; - } - } catch (IOException e) { - Logger.log(LogTag.JFR_SYSTEM_STREAMING, LogLevel.DEBUG, "IOException during repository file scan " + e.getMessage()); - // This can happen if a chunk is being removed - // between the file was discovered and an instance - // of an EventSet was constructed. Just ignore, - // and retry later. - } - try { - synchronized (pathSet) { - pathSet.wait(1000); - } - } catch (InterruptedException e) { - // ignore + if (!waitForPaths()) { + return null; // closed } } - return null; + } + + private void nap() { + try { + synchronized (pathSet) { + pathSet.wait(1000); + } + } catch (InterruptedException e) { + // ignore + } } private boolean updatePaths() throws IOException { @@ -163,7 +167,7 @@ for (Path p : added) { // Only add files that have a complete header // as the JVM may be in progress writing the file - long size = Files.size(p); + long size = fileAccess.fileSize(p); if (size >= ChunkHeader.HEADER_SIZE) { long startNanos = readStartTime(p); pathSet.put(startNanos, p); diff -r c75c241c492a -r 4ce66d271065 test/jdk/jdk/jfr/api/consumer/security/TestMissingPermission.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/jfr/api/consumer/security/TestMissingPermission.java Tue Aug 13 03:58:29 2019 +0200 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.api.consumer.security; + +import java.io.IOException; + +import jdk.jfr.consumer.EventStream; +import jdk.jfr.consumer.RecordingStream; + +/** + * @test + * @summary Tests that streaming doesn't work if + * FlightRecordingPermission("accessFlightRecorder") is missing + * @key jfr + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy + * jdk.jfr.api.consumer.security.TestMissingPermission + */ +public class TestMissingPermission { + + public static void main(String... args) throws Exception { + testOpenRepository(); + testRecordingStream(); + } + + private static void testRecordingStream() throws IOException { + try { + try (EventStream es = EventStream.openRepository()) { + fail(); + } + } catch (SecurityException se) { + // OK, as expected + } + } + + private static void testOpenRepository() throws IOException { + try { + try (RecordingStream es = new RecordingStream()) { + fail(); + } + } catch (SecurityException se) { + // OK, as expected + } + } + + private static void fail() { + throw new AssertionError("Should not be able to create EventStream with FlightRecorderPermission"); + } +} diff -r c75c241c492a -r 4ce66d271065 test/jdk/jdk/jfr/api/consumer/security/TestRecordingFile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/jfr/api/consumer/security/TestRecordingFile.java Tue Aug 13 03:58:29 2019 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.api.consumer.security; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Test that a recording file can't be opened without permissions + * @key jfr + * @requires vm.hasJFR + * @library /test/lib + * @run driver jdk.jfr.api.consumer.security.TestRecordingFile$Dumper + * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy + * jdk.jfr.api.consumer.security.TestRecordingFile + * + */ +public class TestRecordingFile { + public final static Path DUMP_FILE = Paths.get("dump.jfr"); + + public static class Dumper { + public static void main(String... args) throws Exception { + try (Recording r = new Recording()) { + // Enable JVM event, no write permission needed + r.enable(EventNames.JVMInformation); + r.start(); + r.stop(); + r.dump(DUMP_FILE); + } + } + } + + public static void main(String... args) throws Exception { + try { + RecordingFile.readAllEvents(DUMP_FILE); + throw new AssertionError("Expected SecurityException"); + } catch (SecurityException se) { + // OK, as expected + return; + } + } +} diff -r c75c241c492a -r 4ce66d271065 test/jdk/jdk/jfr/api/consumer/security/TestRecordingStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/jfr/api/consumer/security/TestRecordingStream.java Tue Aug 13 03:58:29 2019 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.api.consumer.security; + +import java.time.Instant; +import java.util.concurrent.CountDownLatch; + +import jdk.jfr.consumer.RecordingStream; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Tests that a RecordingStream works using only + * FlightRecordingPermission("accessFlightRecorder") + * @key jfr + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=local-streaming.policy + * jdk.jfr.api.consumer.security.TestStreamingLocal + */ +public class TestRecordingStream { + + public static void main(String... args) throws Exception { + CountDownLatch latch = new CountDownLatch(1); + try (RecordingStream r = new RecordingStream()) { + // Enable JVM event, no write permission needed + r.enable(EventNames.JVMInformation); + r.setStartTime(Instant.EPOCH); + r.onEvent(EventNames.JVMInformation, e -> { + latch.countDown(); + }); + r.startAsync(); + latch.await(); + } + } +} diff -r c75c241c492a -r 4ce66d271065 test/jdk/jdk/jfr/api/consumer/security/TestStreamingFile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingFile.java Tue Aug 13 03:58:29 2019 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.api.consumer.security; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.EventStream; + +/** + * @test + * @summary Test that a event file stream can't be opened without permissions + * @key jfr + * @requires vm.hasJFR + * @library /test/lib + * @build jdk.jfr.api.consumer.security.TestStreamingFile + * @run driver jdk.jfr.api.consumer.security.TestStreamingFile$Dumper + * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=no-permission.policy + * jdk.jfr.api.consumer.security.TestStreamingFile + * + */ +public class TestStreamingFile { + public final static Path DUMP_FILE = Paths.get("dump.jfr"); + + public static class Dumper { + public static void main(String... args) throws Exception { + try (Recording r = new Recording()) { + // Enable JVM event, no write permission needed + r.enable("jdk.JVMInformation"); + r.start(); + r.stop(); + r.dump(DUMP_FILE); + } + } + } + + public static void main(String... args) throws Exception { + try (EventStream es = EventStream.openFile(DUMP_FILE)) { + throw new AssertionError("Expected SecurityException"); + } catch (SecurityException se) { + // OK, as expected + return; + } + } +} diff -r c75c241c492a -r 4ce66d271065 test/jdk/jdk/jfr/api/consumer/security/TestStreamingLocal.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingLocal.java Tue Aug 13 03:58:29 2019 +0200 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.api.consumer.security; + +import java.time.Instant; +import java.util.concurrent.CountDownLatch; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.EventStream; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Tests that local streaming works using only + * FlightRecordingPermission("accessFlightRecorder") + * @key jfr + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=local-streaming.policy + * jdk.jfr.api.consumer.security.TestStreamingLocal + */ +public class TestStreamingLocal { + + public static void main(String... args) throws Exception { + CountDownLatch latch = new CountDownLatch(1); + try (Recording r = new Recording()) { + // Enable JVM event, no write permission needed + r.enable(EventNames.JVMInformation); + r.start(); + r.stop(); + try (EventStream es = EventStream.openRepository()) { + es.setStartTime(Instant.EPOCH); + es.onEvent("jdk.JVMInformation", e -> { + latch.countDown(); + }); + es.startAsync(); + latch.await(); + } + } + + } +} diff -r c75c241c492a -r 4ce66d271065 test/jdk/jdk/jfr/api/consumer/security/TestStreamingRemote.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/jfr/api/consumer/security/TestStreamingRemote.java Tue Aug 13 03:58:29 2019 +0200 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.api.consumer.security; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.time.Instant; + +import jdk.jfr.Event; +import jdk.jfr.Name; +import jdk.jfr.Recording; +import jdk.jfr.consumer.EventStream; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/** + * @test + * @summary Test that a stream can be opened against a remote repository using + * only file permission + * @key jfr + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm jdk.jfr.api.consumer.security.TestStreamingRemote + */ +public class TestStreamingRemote { + + private static final String SUCCESS = "Success!"; + + @Name("Test") + public static class TestEvent extends Event { + } + + public static class Test { + public static void main(String... args) throws Exception { + Path repo = Paths.get(args[0]); + System.out.println("Repository: " + repo); + try (EventStream es = EventStream.openRepository(repo)) { + es.setStartTime(Instant.EPOCH); + es.onEvent(e -> { + System.out.println(SUCCESS); + es.close(); + }); + es.start(); + } + } + } + + public static void main(String... args) throws Exception { + try (Recording r = new Recording()) { + r.setFlushInterval(Duration.ofSeconds(1)); + r.start(); + String repository = System.getProperty("jdk.jfr.repository"); + Path policy = createPolicyFile(repository); + TestEvent e = new TestEvent(); + e.commit(); + String[] c = new String[4]; + c[0] = "-Djava.security.manager"; + c[1] = "-Djava.security.policy=" + escape(policy.toString()); + c[2] = Test.class.getName(); + c[3] = repository; + OutputAnalyzer oa = ProcessTools.executeTestJvm(c); + oa.shouldContain(SUCCESS); + } + } + + private static Path createPolicyFile(String repository) throws IOException { + Path p = Paths.get("permission.policy").toAbsolutePath(); + try (PrintWriter pw = new PrintWriter(p.toFile())) { + pw.println("grant {"); + // All the files and directories the contained in path + String dir = escape(repository); + String contents = escape(repository + File.separatorChar + "-"); + pw.println(" permission java.io.FilePermission \"" + dir + "\", \"read\";"); + pw.println(" permission java.io.FilePermission \"" + contents + "\", \"read\";"); + pw.println("};"); + pw.println(); + } + System.out.println("Permission file: " + p); + for (String line : Files.readAllLines(p)) { + System.out.println(line); + } + System.out.println(); + return p; + } + + // Double quote needed for Windows + private static String escape(String text) { + StringBuilder sb = new StringBuilder(); + for (char c : text.toCharArray()) { + if (c == '\\') { + sb.append(c); + } + sb.append(c); + } + return sb.toString(); + } +} diff -r c75c241c492a -r 4ce66d271065 test/jdk/jdk/jfr/api/consumer/security/local-streaming.policy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/jfr/api/consumer/security/local-streaming.policy Tue Aug 13 03:58:29 2019 +0200 @@ -0,0 +1,4 @@ +// Minimum policy to stream locally +grant { +permission jdk.jfr.FlightRecorderPermission "accessFlightRecorder"; +}; diff -r c75c241c492a -r 4ce66d271065 test/jdk/jdk/jfr/api/consumer/security/no-permission.policy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/jfr/api/consumer/security/no-permission.policy Tue Aug 13 03:58:29 2019 +0200 @@ -0,0 +1,3 @@ +// No permission +grant { +};