|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package jdk.jfr.internal; |
|
|
|
import java.io.File; |
|
import java.io.FileNotFoundException; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.io.RandomAccessFile; |
|
import java.io.Reader; |
|
import java.lang.reflect.Constructor; |
|
import java.lang.reflect.Field; |
|
import java.lang.reflect.Method; |
|
import java.lang.reflect.ReflectPermission; |
|
import java.nio.channels.FileChannel; |
|
import java.nio.channels.ReadableByteChannel; |
|
import java.nio.file.FileVisitResult; |
|
import java.nio.file.Files; |
|
import java.nio.file.Path; |
|
import java.nio.file.Paths; |
|
import java.nio.file.SimpleFileVisitor; |
|
import java.nio.file.StandardOpenOption; |
|
import java.nio.file.attribute.BasicFileAttributes; |
|
import java.security.AccessControlContext; |
|
import java.security.AccessController; |
|
import java.security.Permission; |
|
import java.security.PrivilegedAction; |
|
import java.security.PrivilegedActionException; |
|
import java.security.PrivilegedExceptionAction; |
|
import java.util.ArrayList; |
|
import java.util.Iterator; |
|
import java.util.List; |
|
import java.util.Objects; |
|
import java.util.PropertyPermission; |
|
import java.util.concurrent.Callable; |
|
|
|
import sun.misc.Unsafe; |
|
import jdk.jfr.Event; |
|
import jdk.jfr.FlightRecorder; |
|
import jdk.jfr.FlightRecorderListener; |
|
import jdk.jfr.FlightRecorderPermission; |
|
import jdk.jfr.Recording; |
|
|
|
|
|
|
|
|
|
*/ |
|
public final class SecuritySupport { |
|
private final static Unsafe unsafe = Unsafe.getUnsafe(); |
|
public final static SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr"); |
|
|
|
static final SafePath USER_HOME = getPathInProperty("user.home", null); |
|
static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null); |
|
|
|
final static class SecureRecorderListener implements FlightRecorderListener { |
|
|
|
private final AccessControlContext context; |
|
private final FlightRecorderListener changeListener; |
|
|
|
SecureRecorderListener(AccessControlContext context, FlightRecorderListener changeListener) { |
|
this.context = Objects.requireNonNull(context); |
|
this.changeListener = Objects.requireNonNull(changeListener); |
|
} |
|
|
|
@Override |
|
public void recordingStateChanged(Recording recording) { |
|
AccessController.doPrivileged((PrivilegedAction<Void>) () -> { |
|
try { |
|
changeListener.recordingStateChanged(recording); |
|
} catch (Throwable t) { |
|
|
|
Logger.log(LogTag.JFR, LogLevel.WARN, "Unexpected exception in listener " + changeListener.getClass()+ " at recording state change"); |
|
} |
|
return null; |
|
}, context); |
|
} |
|
|
|
@Override |
|
public void recorderInitialized(FlightRecorder recorder) { |
|
AccessController.doPrivileged((PrivilegedAction<Void>) () -> { |
|
try { |
|
changeListener.recorderInitialized(recorder); |
|
} catch (Throwable t) { |
|
|
|
Logger.log(LogTag.JFR, LogLevel.WARN, "Unexpected exception in listener " + changeListener.getClass()+ " when initializing FlightRecorder"); |
|
} |
|
return null; |
|
}, context); |
|
} |
|
|
|
public FlightRecorderListener getChangeListener() { |
|
return changeListener; |
|
} |
|
} |
|
|
|
private static final class DirectoryCleaner extends SimpleFileVisitor<Path> { |
|
@Override |
|
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { |
|
Files.delete(path); |
|
return FileVisitResult.CONTINUE; |
|
} |
|
|
|
@Override |
|
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { |
|
if (exc != null) { |
|
throw exc; |
|
} |
|
Files.delete(dir); |
|
return FileVisitResult.CONTINUE; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final class SafePath { |
|
private final Path path; |
|
private final String text; |
|
|
|
public SafePath(Path p) { |
|
|
|
text = p.toString(); |
|
path = Paths.get(text); |
|
} |
|
|
|
public SafePath(String path) { |
|
this(Paths.get(path)); |
|
} |
|
|
|
public Path toPath() { |
|
return path; |
|
} |
|
|
|
public String toString() { |
|
return text; |
|
} |
|
} |
|
|
|
private interface RunnableWithCheckedException { |
|
public void run() throws Exception; |
|
} |
|
|
|
private interface CallableWithoutCheckException<T> { |
|
public T call(); |
|
} |
|
|
|
private static <U> U doPrivilegedIOWithReturn(Callable<U> function) throws IOException { |
|
try { |
|
return AccessController.doPrivileged(new PrivilegedExceptionAction<U>() { |
|
@Override |
|
public U run() throws Exception { |
|
return function.call(); |
|
} |
|
}, null); |
|
} catch (PrivilegedActionException e) { |
|
Throwable t = e.getCause(); |
|
if (t instanceof IOException) { |
|
throw (IOException) t; |
|
} |
|
throw new IOException("Unexpected error during I/O operation. " + t.getMessage(), t); |
|
} |
|
} |
|
|
|
private static void doPriviligedIO(RunnableWithCheckedException function) throws IOException { |
|
doPrivilegedIOWithReturn(() -> { |
|
function.run(); |
|
return null; |
|
}); |
|
} |
|
|
|
private static void doPrivileged(Runnable function, Permission... perms) { |
|
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
|
@Override |
|
public Void run() { |
|
function.run(); |
|
return null; |
|
} |
|
}, null, perms); |
|
} |
|
|
|
private static void doPrivileged(Runnable function) { |
|
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
|
@Override |
|
public Void run() { |
|
function.run(); |
|
return null; |
|
} |
|
}); |
|
} |
|
|
|
private static <T> T doPrivilegedWithReturn(CallableWithoutCheckException<T> function, Permission... perms) { |
|
return AccessController.doPrivileged(new PrivilegedAction<T>() { |
|
@Override |
|
public T run() { |
|
return function.call(); |
|
} |
|
}, null, perms); |
|
} |
|
|
|
public static List<SafePath> getPredefinedJFCFiles() { |
|
List<SafePath> list = new ArrayList<>(); |
|
try { |
|
Iterator<Path> pathIterator = doPrivilegedIOWithReturn(() -> { |
|
return Files.newDirectoryStream(JFC_DIRECTORY.toPath(), "*").iterator(); |
|
}); |
|
while (pathIterator.hasNext()) { |
|
Path path = pathIterator.next(); |
|
if (path.toString().endsWith(".jfc")) { |
|
list.add(new SafePath(path)); |
|
} |
|
} |
|
} catch (IOException ioe) { |
|
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not access .jfc-files in " + JFC_DIRECTORY + ", " + ioe.getMessage()); |
|
} |
|
return list; |
|
} |
|
|
|
static void makeVisibleToJFR(Class<?> clazz) { |
|
// nothing to do for JDK8 |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
static void addHandlerExport(Class<?> clazz) { |
|
// nothing to do for JDK8 |
|
} |
|
|
|
public static void registerEvent(Class<? extends Event> eventClass) { |
|
doPrivileged(() -> FlightRecorder.register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT)); |
|
} |
|
|
|
static boolean getBooleanProperty(String propertyName) { |
|
return doPrivilegedWithReturn(() -> Boolean.getBoolean(propertyName), new PropertyPermission(propertyName, "read")); |
|
} |
|
|
|
private static SafePath getPathInProperty(String prop, String subPath) { |
|
return doPrivilegedWithReturn(() -> { |
|
String path = System.getProperty(prop); |
|
if (path == null) { |
|
return null; |
|
} |
|
File file = subPath == null ? new File(path) : new File(path, subPath); |
|
return new SafePath(file.getAbsolutePath()); |
|
}, new PropertyPermission("*", "read")); |
|
} |
|
|
|
|
|
static Thread createRecorderThread(ThreadGroup systemThreadGroup, ClassLoader contextClassLoader) { |
|
// The thread should have permission = new Permission[0], and not "modifyThreadGroup" and "modifyThread" on the stack, |
|
|
|
Thread thread = doPrivilegedWithReturn(() -> new Thread(systemThreadGroup, "JFR Recorder Thread"), new RuntimePermission("modifyThreadGroup"), new RuntimePermission("modifyThread")); |
|
doPrivileged(() -> thread.setContextClassLoader(contextClassLoader), new RuntimePermission("setContextClassLoader"), new RuntimePermission("modifyThread")); |
|
return thread; |
|
} |
|
|
|
static void registerShutdownHook(Thread shutdownHook) { |
|
doPrivileged(() -> Runtime.getRuntime().addShutdownHook(shutdownHook), new RuntimePermission("shutdownHooks")); |
|
} |
|
|
|
static void setUncaughtExceptionHandler(Thread thread, Thread.UncaughtExceptionHandler eh) { |
|
doPrivileged(() -> thread.setUncaughtExceptionHandler(eh), new RuntimePermission("modifyThread")); |
|
} |
|
|
|
static void moveReplace(SafePath from, SafePath to) throws IOException { |
|
doPrivilegedIOWithReturn(() -> Files.move(from.toPath(), to.toPath())); |
|
} |
|
|
|
static void clearDirectory(SafePath safePath) throws IOException { |
|
doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner())); |
|
} |
|
|
|
static SafePath toRealPath(SafePath safePath) throws Exception { |
|
return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath())); |
|
} |
|
|
|
static boolean existDirectory(SafePath directory) throws IOException { |
|
return doPrivilegedIOWithReturn(() -> Files.exists(directory.toPath())); |
|
} |
|
|
|
static RandomAccessFile createRandomAccessFile(SafePath path) throws Exception { |
|
return doPrivilegedIOWithReturn(() -> new RandomAccessFile(path.toPath().toFile(), "rw")); |
|
} |
|
|
|
public static InputStream newFileInputStream(SafePath safePath) throws IOException { |
|
return doPrivilegedIOWithReturn(() -> Files.newInputStream(safePath.toPath())); |
|
} |
|
|
|
public static long getFileSize(SafePath safePath) throws IOException { |
|
return doPrivilegedIOWithReturn(() -> Files.size(safePath.toPath())); |
|
} |
|
|
|
static SafePath createDirectories(SafePath safePath) throws IOException { |
|
Path p = doPrivilegedIOWithReturn(() -> Files.createDirectories(safePath.toPath())); |
|
return new SafePath(p); |
|
} |
|
|
|
public static boolean exists(SafePath safePath) throws IOException { |
|
return doPrivilegedIOWithReturn(() -> Files.exists(safePath.toPath())); |
|
} |
|
|
|
public static boolean isDirectory(SafePath safePath) throws IOException { |
|
return doPrivilegedIOWithReturn(() -> Files.isDirectory(safePath.toPath())); |
|
} |
|
|
|
static void delete(SafePath localPath) throws IOException { |
|
doPriviligedIO(() -> Files.delete(localPath.toPath())); |
|
} |
|
|
|
static boolean isWritable(SafePath safePath) throws IOException { |
|
return doPrivilegedIOWithReturn(() -> Files.isWritable(safePath.toPath())); |
|
} |
|
|
|
static void deleteOnExit(SafePath safePath) { |
|
doPrivileged(() -> safePath.toPath().toFile().deleteOnExit()); |
|
} |
|
|
|
static ReadableByteChannel newFileChannelToRead(SafePath safePath) throws IOException { |
|
return doPrivilegedIOWithReturn(() -> FileChannel.open(safePath.toPath(), StandardOpenOption.READ)); |
|
} |
|
|
|
public static InputStream getResourceAsStream(String name) throws IOException { |
|
return doPrivilegedIOWithReturn(() -> SecuritySupport.class.getResourceAsStream(name)); |
|
} |
|
|
|
public static Reader newFileReader(SafePath safePath) throws FileNotFoundException, IOException { |
|
return doPrivilegedIOWithReturn(() -> Files.newBufferedReader(safePath.toPath())); |
|
} |
|
|
|
static void touch(SafePath path) throws IOException { |
|
doPriviligedIO(() -> new RandomAccessFile(path.toPath().toFile(), "rw").close()); |
|
} |
|
|
|
static void setAccessible(Method method) { |
|
doPrivileged(() -> method.setAccessible(true), new ReflectPermission("suppressAccessChecks")); |
|
} |
|
|
|
static void setAccessible(Field field) { |
|
doPrivileged(() -> field.setAccessible(true), new ReflectPermission("suppressAccessChecks")); |
|
} |
|
|
|
static void setAccessible(Constructor<?> constructor) { |
|
doPrivileged(() -> constructor.setAccessible(true), new ReflectPermission("suppressAccessChecks")); |
|
} |
|
|
|
static void ensureClassIsInitialized(Class<?> clazz) { |
|
unsafe.ensureClassInitialized(clazz); |
|
} |
|
|
|
static Class<?> defineClass(String name, byte[] bytes, ClassLoader classLoader) { |
|
return unsafe.defineClass(name, bytes, 0, bytes.length, classLoader, null); |
|
} |
|
|
|
static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) { |
|
return doPrivilegedWithReturn(() -> new Thread(runnable, threadName), new Permission[0]); |
|
} |
|
|
|
static void setDaemonThread(Thread t, boolean daeomn) { |
|
doPrivileged(()-> t.setDaemon(daeomn), new RuntimePermission("modifyThread")); |
|
} |
|
|
|
public static SafePath getAbsolutePath(SafePath path) throws IOException { |
|
return new SafePath(doPrivilegedIOWithReturn((()-> path.toPath().toAbsolutePath()))); |
|
} |
|
} |