I need to read and modify the 'Compressed' attribute of a file on an NTFS partition from Java. I imagined something in the java.nio.file.attribute package would do it -- hell it's a complex enough package, but I can't find this attribute.
The DosFileAttributes class has getters for the classic hidden/system/readonly/archive attributes only.
I tried Files.readAttributes which allows dynamically retrieving all attributes from a particular "attribute view". Under "dos:*" there was only the same attributes that are already available from the public methods of the DosFileAttributes class. I tried "ntfs:*" and "windows:*" but they weren't accepted as valid view names.
I also tried the UserDefinedFileAttributeView, but it gave me an empty list on any file I tried.
I wondered about shelling out to the attrib command (accepting the limitation that it wouldn't work for NTFS partitions mounted under Linux or other OSes) but that doesn't seem to support the attribute either. Help?
Since this does seem to be missing from the standard Java API, I took a look at doing it myself with JNA. It's my first experience with JNA. It's not as pretty as I'd like and JNA's code seems to be sorely lacking in generics, but it's a hundred times better than mucking about with JNI and trying to set up awful cross-compilers for the different platforms needed (minimum x86 & x64 even if you target only one OS). It's the annoying compilation process that drove me from C++ to Java in the first place, and I hope never to have to return to it.
Anyway, this seems to work. Hopefully it will be useful to someone else too. It provides four public methods:
isAvailable() -- whether or not calling the other methods should work (i.e., we are on Windows and the JNA native lib loaded okay)
isCompressed(File)
setCompressed(File, boolean)
volumeSupportsFileCompression(File) -- asks Windows if the partition where a file is located supports [individual] file compression. E.g., it's true on NTFS, and false on FAT (USB sticks and so on).
Compression in the Windows API is done through a dedicated I/O control operation, and is not merely a "SetAttributes" call. If it were simpler (isomorphic with other file attributes) I'd have put the encryption attribute in there as well for completeness' sake, but anyway.
import java.io.File;
import java.io.IOException;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.ShortByReference;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT.HANDLE;
public class WindowsFileOps {
private WindowsFileOps() {}
private static interface Kernel32Extra extends StdCallLibrary {
int COMPRESSION_FORMAT_NONE = 0x00000000;
int COMPRESSION_FORMAT_DEFAULT = 0x00000001;
int FSCTL_SET_COMPRESSION = 0x0009C040;
Kernel32Extra INSTANCE = (Kernel32Extra)Native.loadLibrary("kernel32",
Kernel32Extra.class, W32APIOptions.UNICODE_OPTIONS);
boolean GetVolumeInformation(
String lpRootPathName,
Pointer lpVolumeNameBuffer,
int nVolumeNameSize,
IntByReference lpVolumeSerialNumber,
IntByReference lpMaximumComponentLength,
IntByReference lpFileSystemFlags,
Pointer lpFileSystemNameBuffer,
int nFileSystemNameSize
);
}
private static Boolean isAvailable;
public static boolean isAvailable() {
if (isAvailable == null) {
try {
isAvailable = Kernel32.INSTANCE != null && Kernel32Extra.INSTANCE != null;
} catch (Throwable t) {
isAvailable = false;
}
}
return isAvailable;
}
private static String pathString(File file) {
// "\\?\" is a Windows API thing that enables paths longer than 260 chars
return "\\\\?\\" + file.getAbsolutePath();
}
private static int getAttributes(File file) throws IOException {
int attrib = Kernel32.INSTANCE.GetFileAttributes(pathString(file));
if (attrib == Kernel32.INVALID_FILE_ATTRIBUTES) {
throw new IOException("Unable to read file attributes of " + file);
}
return attrib;
}
public static boolean isCompressed(File file) throws IOException {
return (getAttributes(file) & Kernel32.FILE_ATTRIBUTE_COMPRESSED) != 0;
}
public static void setCompressed(File file, boolean compressed) throws IOException {
HANDLE hFile = Kernel32.INSTANCE.CreateFile(
pathString(file),
Kernel32.GENERIC_READ | Kernel32.GENERIC_WRITE,
Kernel32.FILE_SHARE_READ,
null,
Kernel32.OPEN_EXISTING,
0,
null);
try {
if (!Kernel32.INSTANCE.DeviceIoControl(
hFile,
Kernel32Extra.FSCTL_SET_COMPRESSION,
new ShortByReference((short)(
compressed
? Kernel32Extra.COMPRESSION_FORMAT_DEFAULT
: Kernel32Extra.COMPRESSION_FORMAT_NONE
)).getPointer(),
2,
null, 0,
new IntByReference(),
null
)) throw new IOException("Unable to alter compression attribute of " + file);
} finally {
Kernel32.INSTANCE.CloseHandle(hFile);
}
}
public static boolean volumeSupportsFileCompression(File file) throws IOException {
IntByReference flags = new IntByReference();
if (!Kernel32Extra.INSTANCE.GetVolumeInformation(
pathString(file.getAbsoluteFile().toPath().getRoot().toFile()),
null, 0,
null,
null,
flags,
null, 0
)) throw new IOException("GetVolumeInformation failure");
return (flags.getValue() & Kernel32.FILE_FILE_COMPRESSION) != 0;
}
}
Instead of DosFileAttributes try use BasicFileAttributes and check isOther() and isRegularFile() flags. That may tell you if file is compressed. If not you would have to create your own FileSystemProvider impl for NTFS or write small JNI code that will use WinAPI to read that flag for you.
Related
I want to figure out which dependent DLLs are missing when loading a DLL fails. By loading a DLL using Java's System#loadLibrary, I only get a long negative exit code such as -1073741515 instead of a Windows error message stating which DLL is missing. This is unhelpful for addressing the problem.
My idea was to parse dependent DLLs from the DLL using Java and loading them one by one to figure out which one throws an UnsatisfiedLinkError. I found a library called pecoff4j which claims to parse PE executables but I'm not able to parse the imported DLL names:
PE pe = PEParser.parse("C:\\Users\\User\\Desktop\\my.dll");
final ImportDirectory importTable = pe.getImageData().getImportTable();
for (int i = 0; i < importTable.size(); i++)
{
String name = importTable.getName(i);
System.out.println(name);
}
This yields the following exception since the names don't seem to be available:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.get(ArrayList.java:433)
at org.boris.pecoff4j.ImportDirectory.getName(ImportDirectory.java:39)
ImportDirectory.java:
package org.boris.pecoff4j;
import org.boris.pecoff4j.util.DataObject;
import java.util.ArrayList;
import java.util.List;
public class ImportDirectory extends DataObject {
private List<ImportDirectoryEntry> entries = new ArrayList();
private List<String> names = new ArrayList();
private List<ImportDirectoryTable> nameTables = new ArrayList();
private List<ImportDirectoryTable> addressTables = new ArrayList();
public void add(ImportDirectoryEntry entry) {
entries.add(entry);
}
public void add(String name, ImportDirectoryTable names,
ImportDirectoryTable addresses) {
this.names.add(name);
nameTables.add(names);
addressTables.add(addresses);
}
public int size() {
return entries.size();
}
public String getName(int index) {
return names.get(index);
}
public ImportDirectoryTable getNameTable(int index) {
return nameTables.get(index);
}
public ImportDirectoryTable getAddressTable(int index) {
return addressTables.get(index);
}
public ImportDirectoryEntry getEntry(int index) {
return entries.get(index);
}
}
Visual Studio's dumpbin works but I need a Java based solution to integrate into an application which is distributed to users who don't necessarily have Visual Studio installed.
If you think there is a better/simpler way to figure out or prevent missing DLLs when loading a DLL using Java, feel free to let me know as well.
I've found a somehow unexpected behaviour using JVM Security Manager custom policies.
repo: https://github.com/pedrorijo91/jvm-sec-manager
in branch master, go into the /code folder:
custom policy file grants File read permission for file ../allow/allow.txt
no permission for the file ../deny/deny.txt
the code in the HelloWorld.java tries to read both files
There's a run.sh script to run the command
Now everything works as expected: the allowed file reads, but the other throws a security exception: java.security.AccessControlException: access denied ("java.io.FilePermission" "../deny/deny.txt" "read")
But if I move both files (../allow/allow.txt and ../deny/deny.txt) to the code folder (changing the custom policy and the java code to use those files), I get no exception. (branch 'unexpected')
Is the current directory a special case or something else is happening?
Brief explanation
This behaviour is documented in a number of places:
FilePermission's overview;
the Permissions in the JDK document; and
the URLClassLoader#getPermissions(CodeSource) method.
The latter two reiterate the closing note of the first one, which states that:
Code can always read a file from the same directory it's in (or a subdirectory of that directory); it does not need explicit permission to do so.
In other words, if
(HelloWorld.class.getProtectionDomain().getCodeSource().implies(
new CodeSource(new URL("file:" + codeDir),
(Certificate[]) null)) == true)
then HelloWorld will by default be granted read access to the denoted directory and its descendants. Particularly for the code directory itself this should make some intuitive sense, as otherwise the class would be unable to even access public-access classes within its very package.
The full story
It is basically up to the ClassLoader: If it statically assigned any Permissions to the ProtectionDomain to which it mapped the class--which applies to both java.net.URLClassLoader and sun.misc.Launcher$AppClassLoader (the OpenJDK-specific default system class loader)--these permissions will always be accorded to the domain, regardless of the Policy in effect.
Workarounds
The typical "quick-n'-dirty" workaround to anything authorization-related is to extend SecurityManager and override the methods irking you; i.e. in this case the checkRead group of methods.
For a more thorough solution that doesn't reduce the flexibility of AccessController and friends, on the other hand, you would have to write a class loader that at the very least overrides URLClassLoader#getPermissions(CodeSource) and/or restricts loaded classes' domains' CodeSources down to the file level (code sources of domains assigned by default by URLClassLoader and AppClassLoader imply (recursively) the .class file's classpath entry (JAR or directory)). For further granularity, your loader might as well assign instances of your own domain subclass, and/or domains encapsulating code sources of your own subclass, overriding respectively ProtectionDomain#implies(Permission) and/or CodeSource#implies(CodeSource); the former could for example be made to support "negative permission" semantics, and the latter could base code source implication on arbitrary logic, potentially decoupled from physical code location (think e.g. "trust levels").
Clarification as per the comments
To prove that under a different class loader these permissions actually matter, consider the following example: There are two classes, A and B; A has the main method, which simply calls a method on B. Additionally, the application is launched using a different system class loader, which a) assigns domains on a per-class basis (rather than on a per-classpath-entry basis, as is the default) to classes it loads, without b) assigning any permissions to these domains.
Loader:
package com.example.q45897574;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Pattern;
public class RestrictiveClassLoader extends URLClassLoader {
private static final Pattern COMMON_SYSTEM_RESOURCE_NAMES = Pattern
.compile("(((net\\.)?java)|(java(x)?)|(sun|oracle))\\.[a-zA-Z0-9\\.\\-_\\$\\.]+");
private static final String OWN_CLASS_NAME = RestrictiveClassLoader.class.getName();
private static final URL[] EMPTY_URL_ARRAY = new URL[0], CLASSPATH_ENTRY_URLS;
private static final PermissionCollection NO_PERMS = new Permissions();
static {
String[] classpathEntries = AccessController.doPrivileged(new PrivilegedAction<String>() {
#Override
public String run() {
return System.getProperty("java.class.path");
}
}).split(File.pathSeparator);
Set<URL> classpathEntryUrls = new LinkedHashSet<>(classpathEntries.length, 1);
for (String classpathEntry : classpathEntries) {
try {
URL classpathEntryUrl;
if (classpathEntry.endsWith(".jar")) {
classpathEntryUrl = new URL("file:jar:".concat(classpathEntry));
}
else {
if (!classpathEntry.endsWith("/")) {
classpathEntry = classpathEntry.concat("/");
}
classpathEntryUrl = new URL("file:".concat(classpathEntry));
}
classpathEntryUrls.add(classpathEntryUrl);
}
catch (MalformedURLException mue) {
}
}
CLASSPATH_ENTRY_URLS = classpathEntryUrls.toArray(EMPTY_URL_ARRAY);
}
private static byte[] readClassData(URL classResource) throws IOException {
try (InputStream in = new BufferedInputStream(classResource.openStream());
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
while (in.available() > 0) {
out.write(in.read());
}
return out.toByteArray();
}
}
public RestrictiveClassLoader(ClassLoader parent) {
super(EMPTY_URL_ARRAY, parent);
for (URL classpathEntryUrl : CLASSPATH_ENTRY_URLS) {
addURL(classpathEntryUrl);
}
}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name == null) {
throw new ClassNotFoundException("< null >", new NullPointerException("name argument must not be null."));
}
if (OWN_CLASS_NAME.equals(name)) {
return RestrictiveClassLoader.class;
}
if (COMMON_SYSTEM_RESOURCE_NAMES.matcher(name).matches()) {
return getParent().loadClass(name);
}
Class<?> ret = findLoadedClass(name);
if (ret != null) {
return ret;
}
return findClass(name);
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String modifiedClassName = name.replace(".", "/").concat(".class");
URL classResource = findResource(modifiedClassName);
if (classResource == null) {
throw new ClassNotFoundException(name);
}
byte[] classData;
try {
classData = readClassData(classResource);
}
catch (IOException ioe) {
throw new ClassNotFoundException(name, ioe);
}
return defineClass(name, classData, 0, classData.length, constructClassDomain(classResource));
}
#Override
protected PermissionCollection getPermissions(CodeSource codesource) {
return NO_PERMS;
}
private ProtectionDomain constructClassDomain(URL codeSourceLocation) {
CodeSource cs = new CodeSource(codeSourceLocation, (Certificate[]) null);
return new ProtectionDomain(cs, getPermissions(cs), this, null);
}
}
A:
package com.example.q45897574;
public class A {
public static void main(String... args) {
/*
* Note:
* > Can't we set the security manager via launch argument?
* No, it has to be set here, or bootstrapping will fail.
* > Why?
* Because our class loader's domain is unprivileged.
* > Can't it be privileged?
* Yes, but then everything under the same classpath entry becomes
* privileged too, because our loader's domain's code source--which
* _its own_ loader creates, thus escaping our control--implies _the
* entire_ classpath entry. There are various workarounds, which
* however fall outside of this example's scope.
*/
System.setSecurityManager(new SecurityManager());
B.b();
}
}
B:
package com.example.q45897574;
public class B {
public static void b() {
System.out.println("success!");
}
}
Unprivileged test:
Make sure nothing is granted at the policy level; then run (assuming a Linux-based OS--modify classpath as appropriate):
java -cp "/home/your_user/classpath/" \
-Djava.system.class.loader=com.example.q45897574.RestrictiveClassLoader \
-Djava.security.debug=access=failure com.example.q45897574.A
You should get a NoClassDefFoundError, along with a failed FilePermission for com.example.q45897574.A.
Privileged test:
Now grant the necessary permission to A (again make sure to correct both the codeBase (code source URL) and permission target name):
grant codeBase "file:/home/your_user/classpath/com/example/q45897574/A.class" {
permission java.io.FilePermission "/home/your_user/classpath/com/example/q45897574/B.class", "read";
};
...and re-run. This time execution should complete successfully.
In my java code I call another 3rd party java class.
I want to catch that latter System.exit() exit code
So I use security-manager as suggested in this post
The problem is that I cannot read files now, I get permissions error
as seen in that post.
How can I catch the exit code and still read files?
Published class MyClass {
class MySecurityManager extends SecurityManager {
#Override
public void checkExit(int status) {
throw new SecurityException();
}
}
public void foo() {
MySecurityManager secManager = new MySecurityManager();
System.setSecurityManager(secManager);
try {
ConfigValidator.main(new String[]{"-dirs", SdkServiceConfig.s.PROPERTIES_FILE_PATH});
new FileInputStream(new File("/Users/eladb/WorkspaceQa/sdk-service/src/main/resources/convert_conditions.sh"));
} catch (SecurityException e) {
//Do something if the external code used System.exit()
String a = "1";
}
catch (Exception e) {
logger.error("failed converting properties file to proto", e);
}
}
}
You have two separate problems: Your trusted code cannot read the file, while the untrusted third-party library can still call System#exit unhindered. The former can be easily circumvented by granting further privileges to the trusted code; the latter is a tad trickier to address.
A bit of background
Privilege assignment
Code (the ProtectionDomains encapsulated by a thread's AccessControlContext) generally gets assigned Permissions in two ways: Statically, by the ClassLoader, upon class definition, and/or dynamically, by the Policy in effect. Other, less frequently encountered alternatives, exist as well: DomainCombiners, for instance, can modify AccessControlContexts' domains (and therefore the effective permissions of their respective code that is subject to authorization) on the fly, and custom domain implementations may use their own logic for permission implication, possibly disregarding or altering the semantics of the policy. By default the permission set of a domain is the union of its static and dynamic permissions. As for how exactly classes are mapped to domains, it is, for the most part, up to the loader's implementation. By default, all classes, JAR'ed or otherwise, residing beneath the same class path entry, are grouped under the same domain. More restrictive class loaders may choose to e.g. allocate a domain per class, which could be used to prevent communication even between classes within the same package.
Privilege evaluation
Under the default SecurityManager, for a privileged operation (an invocation of any method having a SecurityManager#checkXXX within its body) to succeed, every domain (of every class of every method) of the effective AccessControlContext must have been assigned, as explained above, the permission being checked. Recall however that the context need not necessarily represent "the truth" (the actual call stack)—system code gets optimized away early on, while AccessController#doPrivileged calls, along with the DomainCombiner potentially coupled to the AccessControlContext can modify the context's domains, and the authorization algorithm in its entirety, consequently.
Problem and workarounds
The issue with System#exit is that the corresponding permission (RuntimePermission("exitVM.*")) is one amongst few that are statically assigned by the default application class loader (sun.misc.Launcher$AppClassLoader) to all domains associated with classes loaded from the class path.
A number of alternatives come to mind:
Installing a custom SecurityManager which denies the particular right based on, e.g., the class attempting to terminate the JVM process.
Loading the third-party library from a "remote" location (a directory outside of the class path), so that it gets treated as "untrusted" code by its class loader.
Authoring and installing a different application class loader, which does not assign the extraneous permission.
Plugging a custom domain combiner into the access control context, which replaces, at the time of an authorization decision, all third-party domains with equivalent ones that do not have the offending permission.
I should, for the sake of completeness, note that at the Policy level, unfortunately, nothing can be done to negate statically assigned permissions.
The first option is overall the most convenient one, but I will not explore it further because:
The default SecurityManager is quite flexible, thanks to the handful of components (AccessController et al.) it interacts with. The background section in the beginning aimed to serve as a reminder of that flexibility, which "quick-n'-dirty" method overrides tend to cripple.
Careless modifications of the default implementation might cause (system) code to misbehave in curious ways.
Frankly, because it's boring―it's the one-size-fits-all solution perpetually advocated, while the fact that the default manager was standardized in 1.2 for a reason has long been forgotten.
The second alternative is easy to implement but impractical, complicating either development or the build process. Assuming you are not planning to invoke the library solely reflectively, or aided by interfaces present on the class path, it would have to be present initially, during development, and relocated before execution.
The third is, at least in the context of a standalone Java SE application, fairly straightforward and should not pose too much of a burden on performance. It is the approach I will favour herein.
The last option is the most novel and least convenient. It is hard to securely implement, has the greatest potential for performance degradation, and burdens client code with ensuring presence of the combiner prior to every delegation to untrusted code.
Proposed solution
The custom ClassLoader
The following is to be used as the replacement of the default application loader, or alternatively as the context class loader, or the loader used to load at least the untrusted classes. There is nothing novel to this implementation—all it does is prevent delegation to the default application class loader when the class in question is assumed to not be a system one. URLClassLoader#findClass, in turn, does not assign RuntimePermission("exitVM.*") to the domains of the classes it defines.
package com.example.trusted;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.regex.Pattern;
public class ClasspathClassLoader extends URLClassLoader {
private static final Pattern SYSTEM_CLASS_PREFIX = Pattern.compile("((java(x)?|sun|oracle)\\.).*");
public ClasspathClassLoader(ClassLoader parent) {
super(new URL[0], parent);
String[] classpath = System.getProperty("java.class.path").split(File.pathSeparator);
for (String classpathEntry : classpath) {
try {
if (!classpathEntry.endsWith(".jar") && !classpathEntry.endsWith("/")) {
// URLClassLoader assumes paths without a trailing '/' to be JARs by default
classpathEntry += "/";
}
addURL(new URL("file:" + classpathEntry));
}
catch (MalformedURLException mue) {
System.err.println(MessageFormat.format("Erroneous class path entry [{0}] skipped.", classpathEntry));
}
}
}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> ret;
synchronized (getClassLoadingLock(name)) {
ret = findLoadedClass(name);
if (ret != null) {
return ret;
}
if (SYSTEM_CLASS_PREFIX.matcher(name).matches()) {
return super.loadClass(name, resolve);
}
ret = findClass(name);
if (resolve) {
resolveClass(ret);
}
}
return ret;
}
}
If you also wish to fine-tune the domains assigned to loaded classes, you will additionally have to override findClass. The following variant of the loader is a very crude attempt at doing so. constructClassDomain therein merely creates one domain per class path entry (which is more or less the default), but can be modified to do something different.
package com.example.trusted;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public final class ClasspathClassLoader extends URLClassLoader {
private static final Pattern SYSTEM_CLASS_PREFIX = Pattern.compile("((java(x)?|sun|oracle)\\.).*");
private static final List<WeakReference<ProtectionDomain>> DOMAIN_CACHE = new ArrayList<>();
// constructor, loadClass same as above
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
URL classOrigin = getClassResource(name);
if (classOrigin == null) {
return super.findClass(name);
}
URL classCodeSourceOrigin = getClassCodeSourceResource(classOrigin);
if (classCodeSourceOrigin == null) {
return super.findClass(name);
}
return defineClass(name, readClassData(classOrigin), constructClassDomain(classCodeSourceOrigin));
}
private URL getClassResource(String name) {
return AccessController.doPrivileged((PrivilegedAction<URL>) () -> getResource(name.replace(".", "/") + ".class"));
}
private URL getClassCodeSourceResource(URL classResource) {
for (URL classpathEntry : getURLs()) {
if (classResource.getPath().startsWith(classpathEntry.getPath())) {
return classpathEntry;
}
}
return null;
}
private ByteBuffer readClassData(URL classResource) {
try {
BufferedInputStream in = new BufferedInputStream(classResource.openStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
int i;
while ((i = in.read()) != -1) {
out.write(i);
}
return ByteBuffer.wrap(out.toByteArray());
}
catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
private ProtectionDomain constructClassDomain(URL classCodeSourceResource) {
ProtectionDomain ret = getCachedDomain(classCodeSourceResource);
if (ret == null) {
CodeSource cs = new CodeSource(classCodeSourceResource, (Certificate[]) null);
DOMAIN_CACHE.add(new WeakReference<>(ret = new ProtectionDomain(cs, getPermissions(cs), this, null)));
}
return ret;
}
private ProtectionDomain getCachedDomain(URL classCodeSourceResource) {
for (WeakReference<ProtectionDomain> domainRef : DOMAIN_CACHE) {
ProtectionDomain domain = domainRef.get();
if (domain == null) {
DOMAIN_CACHE.remove(domainRef);
}
else if (domain.getCodeSource().implies(new CodeSource(classCodeSourceResource, (Certificate[]) null))) {
return domain;
}
}
return null;
}
}
The "unsafe" code
package com.example.untrusted;
public class Test {
public static void testExitVm() {
System.out.println("May I...?!");
System.exit(-1);
}
}
The entry point
package com.example.trusted;
import java.security.AccessControlException;
import java.security.Permission;
import com.example.untrusted.Test;
public class Main {
private static final Permission EXIT_VM_PERM = new RuntimePermission("exitVM.*");
public static void main(String... args) {
System.setSecurityManager(new SecurityManager());
try {
Test.testExitVm();
}
catch (AccessControlException ace) {
Permission deniedPerm = ace.getPermission();
if (EXIT_VM_PERM.implies(deniedPerm)) {
ace.printStackTrace();
handleUnauthorizedVmExitAttempt(Integer.parseInt(deniedPerm.getName().replace("exitVM.", "")));
}
}
}
private static void handleUnauthorizedVmExitAttempt(int exitCode) {
System.out.println("here let me do it for you");
System.exit(exitCode);
}
}
Testing
Packaging
Place the loader and the main class in one JAR (let's call it trusted.jar) and the demo untrusted class in another (untrusted.jar).
Assigning privileges
The default Policy (sun.security.provider.PolicyFile) is backed by the file at <JRE>/lib/security/java.policy, as well as any of the files referenced by the policy.url.n properties in <JRE>/lib/security/java.security. Modify the former (the latter should hopefully be empty by default) as follows:
// Standard extensions get all permissions by default
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};
// no default permissions
grant {};
// trusted code
grant codeBase "file:///path/to/trusted.jar" {
permission java.security.AllPermission;
};
// third-party code
grant codeBase "file:///path/to/untrusted.jar" {
permission java.lang.RuntimePermission "exitVM.-1", "";
};
Note that it is virtually impossible to get components extending the security infrastructure (custom class loaders, policy providers, etc.) to work properly without granting them AllPermission.
Running
Run:
java -classpath "/path/to/trusted.jar:/path/to/untrusted.jar" -Djava.system.class.loader=com.example.trusted.ClasspathClassLoader com.example.trusted.Main
The privileged operation should succeed.
Next comment out the RuntimePermission under untrusted.jar, within the policy file, and re-run. The privileged operation should fail.
As a closing note, when debugging AccessControlExceptions, running with -Djava.security.debug=access=domain,access=failure,policy can help track down offending domains and policy configuration issues.
I'm trying to analyse some bits of Java-code, looking if the code is written too complexly. I start with a String containing the contents of a Java-class.
From there I want to retrieve, given a function-name, the "inner code" by that function. In this example:
public class testClass{
public int testFunction(char x) throws Exception{
if(x=='a'){
return 1;
}else if(x=='{'){
return 2;
}else{
return 3;
}
}
public int testFunctionTwo(int y){
return y;
}
}
I want to get, when I call String code = getcode("testFunction");, that code contains if(x=='a'){ ... return 3; }. I've made the input code extra ugly, to demonstrate some of the problems one might encounter when doing character-by-character-analysis (because of the else if, the curly brackets will no longer match, because of the Exception thrown, the function declaration is not of the form functionName{ //contents }, etc.)
Is there a solid way to get the contents of testFunction, or should I implement all problems described manually?
You need to a java parser. I worked too with QDox. it is easy to use. example here:
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;
import java.io.File;
import java.io.IOException;
public class Parser {
public void parseFile() throws IOException {
File file = new File("/path/to/testClass.java");
JavaProjectBuilder builder = new JavaProjectBuilder();
builder.addSource(file);
for (JavaClass javaClass : builder.getClasses()) {
if (javaClass.getName().equals("testClass")) {
for (JavaMethod javaMethod : javaClass.getMethods()) {
if (javaMethod.getName().equals("testMethod")) {
System.out.println(javaMethod.getSourceCode());
}
}
}
}
}
}
Have you considered using a parser to read your code? There are a lot of parsers out there, the last time I worked on a problem like this http://qdox.codehaus.org made short work of these kinds of problems.
I am developing an application which monitors the presence of the power supply of the laptop. If there is a power cut or restoration it will intimate me over email. It will also application monitoring and controlling over email (Basically to control my laptop from my office over email). I am done with email interfacing but I have no idea on how to monitor the power supply / battery supply from java.
If any can give some pointer on this it will be of great help.
Thanks in advance ....
You have probably already solved this problem but for the others - you can do it the way Adam Crume suggests, using an already written script battstat.bat for Windows XP and higher.
Here is an example of the resulting function:
private Boolean runsOnBattery() {
try {
Process proc = Runtime.getRuntime().exec("cmd.exe /c battstat.bat");
BufferedReader stdInput = new BufferedReader(
new InputStreamReader(proc.getInputStream()));
String s;
while ((s = stdInput.readLine()) != null) {
if (s.contains("mains power")) {
return false;
} else if (s.contains("Discharging")) {
return true;
}
}
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
return false;
}
Or you can simplify the script to return directly True/False or whatever fits.
On linux, you can use /proc/acpi/battery/
A quick google search turns up a java acpi library on sourceforge. Hasn't been updated since 2004 though.
Here's code that works on Windows by using the SYSTEM_POWER_STATUS structure.
Note that you need to add jna to your (Maven) dependencies for this to work.
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.win32.StdCallLibrary;
public interface Kernel32 extends StdCallLibrary
{
public Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("Kernel32",
Kernel32.class);
public class SYSTEM_POWER_STATUS extends Structure
{
public byte ACLineStatus;
#Override
protected List<String> getFieldOrder()
{
ArrayList<String> fields = new ArrayList<String>();
fields.add("ACLineStatus");
return fields;
}
public boolean isPlugged()
{
return ACLineStatus == 1;
}
}
public int GetSystemPowerStatus(SYSTEM_POWER_STATUS result);
}
In your code call it like this:
Kernel32.SYSTEM_POWER_STATUS batteryStatus = new Kernel32.SYSTEM_POWER_STATUS();
Kernel32.INSTANCE.GetSystemPowerStatus(batteryStatus);
System.out.println(batteryStatus.isPlugged());
Result:
true if charger is plugged in false otherwise
This has been derived off BalsusC's answer.
A quick and dirty way to handle this is call a native program (via Runtime.exec(...)) and parse the output. On Windows, the native program might be VBScript that uses WMI.