I have method, which using sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess().get(FileDescriptor) by Java 8 for getting the real POSIX file descriptor. In Java 9 (and upper) SharedSecrets was migrated to jdk.internal.misc.
How can I get POSIX file descriptor in Java 11?
private int getFileDescriptor() throws IOException {
final int fd = SharedSecrets.getJavaIOFileDescriptorAccess().get(getFD());
if(fd < 1)
throw new IOException("failed to get POSIX file descriptor!");
return fd;
}
Thanks in advance!
This is only to be used in case of emergency (or until you find a different way since this is not supported) because it does things unintended by the API and is not supported. Caveat emptor.
package sandbox;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
public class GetFileHandle {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("somedata.txt")) {
FileDescriptor fd = fis.getFD();
Field field = fd.getClass().getDeclaredField("fd");
field.setAccessible(true);
Object fdId = field.get(fd);
field.setAccessible(false);
field = fd.getClass().getDeclaredField("handle");
field.setAccessible(true);
Object handle = field.get(fd);
field.setAccessible(false);
// One of these will be -1 (depends on OS)
// Windows uses handle, non-windows uses fd
System.out.println("fid.handle="+handle+" fid.fd"+fdId);
} catch (IOException | NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
Related
I'm trying to prevent plugins that run "inside" the main Java application from accessing things they shouldn't. I've read about Policies, and AccessControllers, and ProtectionDomains, but they're very oriented around JARs.
I've tried this:
import java.nio.file.Files
import java.nio.file.Paths
import java.security.*
fun main(args: Array<String>) {
Policy.setPolicy(object : Policy() {})
System.setSecurityManager(SecurityManager())
val domain = ProtectionDomain(null, Permissions() /* no permissions */)
AccessController.doPrivileged(PrivilegedAction {
untrusted()
}, AccessControlContext(arrayOf(domain)))
}
fun untrusted() {
try {
// Works as expected
Files.readAllBytes(Paths.get("build.gradle"))
throw IllegalStateException("Was able to access file, but shouldn't have been able to")
} catch (e: AccessControlException) {
}
try {
// Should throw AccessControlException, but doesn't
AccessController.doPrivileged(PrivilegedAction {
Files.readAllBytes(Paths.get("build.gradle"))
})
throw IllegalStateException("Was able to access file, but shouldn't have been able to")
} catch (e: AccessControlException) {
}
}
Even though I'm invoking untrusted() via a custom limited ProtectionDomain, it seems it can trivially break out of it. I'm expecting the the doPrivileged call in untrusted to operate with the intersection of the permissions of the outermost ProtectionDomain (the main program, which has all permissions) and the caller's ProtectionDomain (which has no permissions), resulting in untrusted having essentially 0 permissions.
I've also tried with the domain set like this:
val domain = ProtectionDomain(CodeSource(URL("http://foo"), null as Array<CodeSigner>?), Permissions() /* no permissions */)
but this also doesn't work -- the Policy is queried with the main program's ProtectionDomain and not the one calling untrusted(). (Obviously I'd need to update the Policy to handle "http://foo" correctly, but it doesn't even check that ProtectionDomain anyway)
So where has my understanding gone wrong?
After doing some research on this, I think I have an answer. I could write a significantly longer answer, but I think I'll just cut to the chase here.
Each class loaded by a ClassLoader has a ProtectionDomain+CodeSource associated with it. These are somewhat coarse -- a CodeSource represents where a class came from, but it's not a pointer to an individual .class file or anything -- it's to a directory or a JAR. Thus two classes in the same JAR or directory will generally have identical permissions. Any class or script that has an identifiable ProtectionDomain+CodeSource can be whitelisted/blacklisted by your Policy.
The exception (kinda) to this is, of course, is AccessController.doPrivileged with Permission arguments. This lets you clamp down the permissions of a region of code. But that code could, in theory, call AccessController.doPrivileged with just the callback. That method signature means "don't check my entire call stack for permissions; just look up my ProtectionDomain+CodeSource in the Policy file and see what it says." So if you're running truly untrusted code, you better make sure that a. it has a ProtectionDomain+CodeSource different from your trusted application, and b. that your Policy is able to identify that code and grant it appropriately-limited permissions.
Here is one way for the example to run as intended, i.e., to effectively blacklist subsequent execution paths under the same domain. The core permission-intersection-based authorization model should still hold. The sample must be run with -Djava.system.class.loader=com.example.Test$AppClassLoader (this replacement system class loader is only needed in order to attain a working single-file example).
Obligatory disclaimer: While technically many things are possible, to the point of dynamically white-/blacklisting individual instances and beyond, they all involve additional context of some sort being introduced into the already non-trivial authorization process. Such approaches should be avoided whenever possible. The proper solution, sufficing in the vast majority of cases, as documented in the OP's answer's conclusion, is to package trusted code separately from untrusted (and, when manually managing class-to-domain mappings, ensuring that code bases of distinct trustworthiness are mapped to distinct domains), and assign appropriate permissions to the resulting domains.
package com.example;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
public static final class AppClassLoader extends URLClassLoader {
private static final URL[] CLASS_PATH;
private static final String SANDBOXABLE_DOMAIN_CLASS_NAME = "com.example.Test$SandboxableDomain";
static {
String[] paths = System.getProperty("java.class.path").split(File.pathSeparator);
List<URL> classPath = new ArrayList<>();
for (String path : paths) {
try {
classPath.add(new URL("file://" + path));
}
catch (MalformedURLException ex) {}
}
CLASS_PATH = classPath.toArray(new URL[0]);
}
private final Constructor<?> sandboxableDomainCtor;
{
try {
// ensure this loader defines SandboxableDomain so that normal code
// can safely / conveniently access it via class literal
Class<?> sandboxableDomainClass = loadClass(SANDBOXABLE_DOMAIN_CLASS_NAME, true);
sandboxableDomainCtor = sandboxableDomainClass.getConstructor(CodeSource.class,
PermissionCollection.class, ClassLoader.class);
}
catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
public AppClassLoader(ClassLoader parent) {
super(CLASS_PATH, parent);
}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith("java") || name.startsWith("sun")) {
return super.loadClass(name, resolve);
}
Class<?> ret = findLoadedClass(name);
if (ret != null) {
return ret;
}
ProtectionDomain assignedDomain;
byte[] classData;
try {
URL classResource = getResource(name.replace(".", "/") + ".class");
CodeSource assignedCodeSource = new CodeSource(classResource, (Certificate[]) null);
classData = Files.readAllBytes(Paths.get(classResource.toURI()));
if (SANDBOXABLE_DOMAIN_CLASS_NAME.equals(name)) {
// loading the domain class itself; ensure _its own_ domain is fully privileged,
// so that it doesn't affect authorization
PermissionCollection perms = new Permissions();
perms.add(new AllPermission());
assignedDomain = new ProtectionDomain(assignedCodeSource, perms, this, null);
}
else {
// the per-class code source (URL) is unintentional; normally all classes under
// the same class path entry would share one
assignedDomain = (ProtectionDomain) sandboxableDomainCtor.newInstance(assignedCodeSource,
getPermissions(assignedCodeSource), this);
}
}
catch (NullPointerException | URISyntaxException | IOException | ReflectiveOperationException ex) {
throw new ClassNotFoundException(name);
}
ret = defineClass(name, classData, 0, classData.length, assignedDomain);
if (resolve) {
resolveClass(ret);
}
return ret;
}
}
public static final class SandboxableDomain extends ProtectionDomain {
private static final Permission DO_SANDBOXED_PERM = new RuntimePermission("com.example.doSandboxed");
private final ThreadLocal<Boolean> sandboxed = new InheritableThreadLocal<>();
public SandboxableDomain(CodeSource cs, PermissionCollection permissions, ClassLoader classLoader) {
super(cs, permissions, classLoader, null);
sandboxed.set(false);
}
// no equivalent doUnsandboxed here for escaping the sandbox on-demand;
// firstly because it's fishy; secondly because it would be impossible
// to distinguish a privileged caller based on permissions alone
public void doSandboxed(Runnable action) {
if (!sandboxed.get()) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(DO_SANDBOXED_PERM);
}
}
sandboxed.set(true);
try {
action.run();
}
finally {
sandboxed.set(false);
}
}
#Override
public boolean implies(Permission permission) {
if (sandboxed.get()) {
// static only (AppClassLoader only grants essentials like reading from own directory)
PermissionCollection perms = getPermissions();
return (perms == null) ? false : perms.implies(permission);
}
// static + policy
return super.implies(permission);
}
}
public static void main(String[] args) throws Exception {
initSecurity();
SandboxableDomain ownDomain = (SandboxableDomain) Test.class.getProtectionDomain();
System.out.println("Try unsandboxed"); // should succeed
untrusted();
System.out.println("---\n\nTry sandboxed"); // should fail
ownDomain.doSandboxed(Test::untrusted);
System.out.println("---\n\nTry unsandboxed from within a child thread"); // should succeed
new Thread(Test::untrusted).start();
Thread.sleep(1000);
System.out.println("---\n\nTry unsandboxed from within a sandboxed child thread"); // should fail
ownDomain.doSandboxed(() -> new Thread(Test::untrusted).start());
}
private static void initSecurity() throws Exception {
Path tempPolicyConfig = Files.createTempFile(null, null);
// self-grant AllPermission
Files.write(tempPolicyConfig,
Collections.singletonList(new StringBuilder("grant codebase \"")
.append(Test.class.getProtectionDomain().getCodeSource().getLocation()).append("\"{permission ")
.append(AllPermission.class.getName()).append(";};").toString()));
System.setProperty("java.security.policy", "=" + tempPolicyConfig.toString());
System.setSecurityManager(new SecurityManager());
Files.delete(tempPolicyConfig);
}
private static void untrusted() {
try {
untrusted0();
System.out.println("\tSucceeded");
}
catch (AccessControlException ex) {
System.out.println("\tFailed; try via doPrivileged");
try {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
untrusted0();
return null;
});
System.out.println("\t\tSucceeded");
}
catch (AccessControlException ex1) {
System.out.println("\t\tFailed anew");
}
}
}
private static void untrusted0() {
try {
Files.readAllBytes(Paths.get("build.gradle"));
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
I'm trying to load a file from resources/ path using
getClassLoader().getResourceAsStream("file.LIB")
but the method always returns null, unless I rename the file into another extension, say ".dll".
I've looked into the official Java documentation, but to no avail.
Why does the method acts strange on that file type?
Note: I'm using JDK 1.8.0_111 x86 (due to constraints on that lib file, which only works well with a 32-bit JVM)
It does works for me, you need to be sure what exactly you are doing with lib file.
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class FileHelper {
public String getFilePathToSave() {
Properties prop = new Properties();
String filePath = "";
try {
InputStream inputStream =
getClass().getClassLoader().getResourceAsStream("abc.lib");
prop.load(inputStream);
filePath = prop.getProperty("json.filepath");
} catch (IOException e) {
e.printStackTrace();
}
return filePath;
}
public static void main(String args[]) {
FileHelper fh = new FileHelper();
System.out.println(fh.getFilePathToSave());
}
}
I'm writing an implementation of setsockopt under JNA. Java itself supports setsockopt, but it doesn't support all the platform specific socket options. For instance, it doesn't support [TCP_KEEPIDLE][2] under Linux. Clearly, many of these options are not very portable, and using JNA is a route to poor portability; I am aware of this. Please don't bother to tell me the idea is deeply horrible.
What I'd like to do, however, is make my code a little more reuseable than something that just works under Linux. I'd like it to work (as far as is possible) on several target platforms. If the socket option is not available, it can throw an exception.
My challenge is this. The JNA works fine, but the values of the socket options are different across platforms. For instance, SO_RCVBUF is 0x1002 under OS-X and 8 under Linux (I realise SO_RCVBUF is controllable by the normal Java setSockOpt - it's an example that's easy to test with lsof). SO_DONTROUTE is 5 under Linux, and 0x0010 under OS-X (and that isn't controllable via Java setSockOpt).
So what I'd like it to do is to take an enum value representing the socket option (SO_SNDBUF, SO_RCVBUF or whatever), and look that up in a platform dependent map, so I get 0x1002 / 0x010 under OS-X and 8 / 5 under Linux.
That's easy enough, but how do I tell what the platform is under JNA so I know which map to use? JNA must somehow have a sense of its own platform, or it would not (I presume) know how to call the native libraries.
package sockettest;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.Socket;
import com.sun.jna.LastErrorException;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
public class JNASockOpt {
private static Field fdField;
static {
Native.register("c");
try {
fdField = FileDescriptor.class.getDeclaredField("fd");
fdField.setAccessible(true);
} catch (Exception ex) {
fdField = null;
}
}
public static int getInputFd(Socket s) {
try {
FileInputStream in = (FileInputStream)s.getInputStream();
FileDescriptor fd = in.getFD();
return fdField.getInt(fd);
} catch (Exception e) { }
return -1;
}
public static int getOutputFd(Socket s) {
try {
FileOutputStream in = (FileOutputStream)s.getOutputStream();
FileDescriptor fd = in.getFD();
return fdField.getInt(fd);
} catch (Exception e) { }
return -1;
}
public static int getFd(Socket s) {
int fd = getInputFd(s);
if (fd != -1)
return fd;
return getOutputFd(s);
}
// The list of SOL_ and SO_ options is platform dependent
public static final int SOL_SOCKET = 0xffff; // that's under OS-X, but it's 1 under Linux
public static final int SO_RCVBUF = 0x1002; // that's under OS-X, but it's 8 under Linux
public static final int SO_DONTROUTE = 0x0010; // that's under OS-X, but it's 5 under Linux
private static native int setsockopt(int fd, int level, int option_name, Pointer option_value, int option_len) throws LastErrorException;
public static void setSockOpt (Socket socket, int level, int option_name, int option_value) throws IOException {
if (socket == null)
throw new IOException("Null socket");
int fd = getFd(socket);
if (fd == -1)
throw new IOException("Bad socket FD");
IntByReference val = new IntByReference(option_value);
try {
setsockopt(fd, level, option_name, val.getPointer(), 4);
} catch (LastErrorException ex) {
throw new IOException("setsockopt: " + strerror(ex.getErrorCode()));
}
}
public static native String strerror(int errnum);
private JNASockOpt() {
}
}
The class com.sun.jna.Platform provided by JNA is used by JNA itself and has functions for querying the OS family and CPU architecture.
There are static methods for isMac() and isLinux().
jnaplatform does this by string parsing System.getProperty("os.name");, which seems pretty horrible to me, but if jnaplatform does it, I guess that should be good enough for me.
Results (i.e. how I used the above idea to solve the issue in the question) at https://github.com/abligh/jnasockopt - specifically https://github.com/abligh/jnasockopt/blob/master/src/org/jnasockopt/JNASockOptionDetails.java
Is it possible to hide or remove java api's from nashorn-engine?
So that it could only see or use "default" ECMAScript 262 Edition 5.1 with some especially exposed functions / variables?
I would like to let my endusers create some specific logic for their own without worrying they could hack the whole system. Of course there might be some security holes in the nashorn engine etc. but that is the different topic.
Edit: Sorry I forgot to mention that I am running nashorn inside my java application, so no commandline parameters can be used.
Programmatically, you can also directly use the NashornScriptEngineFactory class which has an appropriate getScriptEngine() method:
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
...
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
...
ScriptEngine engine = factory.getScriptEngine("-strict", "--no-java", "--no-syntax-extensions");
OK, here is sample class with some limiting arguments:
package com.pasuna;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Random;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
public class ScriptTest {
public static class Logger {
public void log(String message) {
System.out.println(message);
}
}
public static class Dice {
private Random random = new Random();
public int D6() {
return random.nextInt(6) + 1;
}
}
public static void main(String[] args) {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine(new String[]{"-strict", "--no-java", "--no-syntax-extensions"});
//note final, does not work.
final Dice dice = new Dice();
final Logger logger = new Logger();
engine.put("dice", dice);
engine.put("log", logger);
engine.put("hello", "world");
try {
engine.eval("log.log(hello);");
engine.eval("log.log(Object.keys(this));");
engine.eval("log.log(dice.D6());"
+ "log.log(dice.D6());"
+ "log.log(dice.D6());");
engine.eval("log.log(Object.keys(this));");
engine.eval("Coffee"); //boom as should
engine.eval("Java"); //erm? shoud boom?
engine.eval("log = 1;"); //override final, boom, nope
engine.eval("log.log(hello);"); //boom
} catch (final ScriptException ex) {
ex.printStackTrace();
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String input = "";
do {
try {
input = br.readLine();
engine.eval(input);
} catch (final ScriptException | IOException se) {
se.printStackTrace();
}
} while (!input.trim().equals("quit"));
try {
engine.eval("var add = function(first, second){return first + second;};");
Invocable invocable = (Invocable) engine;
Object result = invocable.invokeFunction("add", 1, 2);
System.out.println(result);
} catch (final NoSuchMethodException | ScriptException se) {
se.printStackTrace();
}
Object l = engine.get("log");
System.out.println(l == logger);
}
}
more info about flags can be found from here: http://hg.openjdk.java.net/jdk8/jdk8/nashorn/rev/eb7b8340ce3a
(imho atm the nashorn documentation is poor)
You can specify any jjs option for script engines via -Dnashorn.args option when you launch your java program. For example:
java -Dnashorn.args=--no-java Main
where Main uses javax.script API with nashorn engine.
You can run "jjs" tool with --no-java option to prevent any explicit Java package/class access from scripts. That said Nashorn platform is secure and uses Java standard URL codebase based security model ('eval'-ed script without known URL origin is treated like untrusted, unsigned code and so gets only sandbox permissions.
--no-java is the main flag to turn off java extensions. --no-syntax-extensions turns off non-standard extensions.
I am running java 7 applications on unix machines. Is there a way to get the current umask value in pure java ?
In C I would use a combination of umask system calls, but I don't think I can call that in Java without resorting to JNI. Is there another approach ?
Edit: Here is a C example (from GUN libc docs):
mode_t
read_umask (void)
{
mode_t mask = umask (0);
umask (mask);
return mask;
}
A simple solution, if there is no Class/Method to get the umask, why don't you get it before call java and pass as a property?
Can you clarify? Do you want to read the umask of the application(the current java process)? Or do you want to read the umask value of some files on the file system?
You can use NIO (the used code is from the javadocs) to get some file attributes, or you can execute a shell command, since the process created with Runtime.execute inherits the umask of it's creator process.
So you should be able to solve your problem without the use of JNI.
package test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermissions;
public class Test {
private static final String COMMAND = "/bin/bash -c umask -S";
public static String getUmask() {
final Runtime runtime = Runtime.getRuntime();
Process process = null;
try {
process = runtime.exec(COMMAND);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String umask = reader.readLine();
if (process.waitFor() == 0)
return umask;
} catch (final IOException e) {
e.printStackTrace();
} catch (final InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
return "";
}
public static void main(String[] args) throws IOException {
/*
* NIO
*/
PosixFileAttributes attrs = Files.getFileAttributeView(Paths.get("testFile"), PosixFileAttributeView.class)
.readAttributes();
System.out.format("%s %s%n", attrs.owner().getName(), PosixFilePermissions.toString(attrs.permissions()));
/*
* execute shell command to get umask of current process
*/
System.out.println(getUmask());
}
}