aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/com/jogamp/common/util
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2013-06-11 16:25:48 +0200
committerSven Gothel <[email protected]>2013-06-11 16:25:48 +0200
commit1a01dce6c42b398cdd68d405828774a3ab366456 (patch)
treedcbc917b0dbd80c7c5be0b4a9ad35c5489ee64dc /src/java/com/jogamp/common/util
parent377d9de1ff1e2fabcd9bb7f65c0318f3c890392c (diff)
Bug 752: Review Code Vulnerabilities (Permission Checks of new exposed code and privileged access)
This review focuses on how we perform permission checks, or better - do we circumvent some assuming full privileges ? Some native methods do need extra permission validation, i.e. loading native libraries. Further more AccessController.doPrivileged(..) shall not cover generic code exposing a critical feature to the user. Further more .. we should rely on the SecuritManager, i.e. AccessControlContext's 'checkPermission(Permission)' code to comply w/ fine grained permission access. It is also possible to have full permission w/o having any certificates (-> policy file). +++ We remove implicit AccessController.doPrivileged(..) from within our trusted code for generic methods, like Property access, temp. files. +++ SecurityUtil: - Remove 'getCommonAccessControlContext(Class<?> clz)', which returned a local AccessControlContext for later restriction if the passed class contains all certificates as the 'trusted' GlueGen class has. - Simply expose convenient permission check methods relying on SecurityManager / AccessControlContext. PropertyAccess: - 'protected static void addTrustedPrefix(..)' requires AllPermissions if SecurityManager is installed. - Remove implicit doPrivileged(..) triggered by passed AccessControlContext instance, only leave it for trusted prefixes. IOUtil: - Remove all doPrivileged(..) - Elevation shall be performed by caller. DynamicLinker: - 'public long openLibraryLocal(..)' and 'public long openLibraryGlobal(..)' may throw SecurityException, if a SecurityManager is installed and the dyn. link permission is not granted in the calling code. Implemented in their respective Unix, OSX and Windows manifestation. Caller has to elevate privileges via 'doPrivileged(..) {}' ! +++ Tests: - Property access - File access - Native library loading Manual Applet test (unsigned, but w/ SecurityManager and policy file): > gluegen/test/applet Applet has been tested w/ signed JAR w/ Firefox and Java7 on GNU/Linux as well. Manual Application test (unsigned, but w/ SecurityManager and policy file): com.jogamp.junit.sec.TestSecIOUtil01 - Run w/ SecurityManager and policy file: - gluegen/scripts/runtest-secmgr.sh - Run w/o SecurityManager: - gluegen/scripts/runtest.sh
Diffstat (limited to 'src/java/com/jogamp/common/util')
-rw-r--r--src/java/com/jogamp/common/util/IOUtil.java100
-rw-r--r--src/java/com/jogamp/common/util/PropertyAccess.java67
-rw-r--r--src/java/com/jogamp/common/util/SecurityUtil.java141
-rw-r--r--src/java/com/jogamp/common/util/cache/TempFileCache.java8
4 files changed, 178 insertions, 138 deletions
diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java
index 46d6b24..2f0c77f 100644
--- a/src/java/com/jogamp/common/util/IOUtil.java
+++ b/src/java/com/jogamp/common/util/IOUtil.java
@@ -33,12 +33,10 @@ import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.lang.reflect.Constructor;
import java.net.URI;
import java.net.URISyntaxException;
@@ -817,20 +815,12 @@ public class IOUtil {
* @param dir the directory to process
* @param create true if the directory shall be created if not existing
* @param executable true if the user intents to launch executables from the temporary directory, otherwise false.
- * @param acc The security {@link AccessControlContext} to create directories and test <i>executability</i>
* @throws SecurityException if file creation and process execution is not allowed within the current security context
*/
- public static File testDir(final File dir, final boolean create, final boolean executable, AccessControlContext acc)
+ public static File testDir(final File dir, final boolean create, final boolean executable)
throws SecurityException
{
- if( null != acc ) {
- return AccessController.doPrivileged(new PrivilegedAction<File>() {
- public File run() {
- return testDirImpl(dir, create, executable);
- } }, acc);
- } else {
- return testDirImpl(dir, create, executable);
- }
+ return testDirImpl(dir, create, executable);
}
private static boolean isStringSet(String s) { return null != s && 0 < s.length(); }
@@ -873,9 +863,35 @@ public class IOUtil {
return tmpBaseDir;
}
- private static File getTempDirImpl(boolean executable)
+ /**
+ * Returns a platform independent writable directory for temporary files
+ * consisting of the platform's {@code temp-root} + {@link #tmpSubDir},
+ * e.g. {@code /tmp/jogamp_0000/}.
+ * <p>
+ * On standard Java the {@code temp-root} folder is specified by <code>java.io.tempdir</code>.
+ * </p>
+ * <p>
+ * On Android the {@code temp-root} folder is relative to the applications local folder
+ * (see {@link Context#getDir(String, int)}) is returned, if
+ * the Android application/activity has registered it's Application Context
+ * via {@link jogamp.common.os.android.StaticContext.StaticContext#init(Context, ClassLoader) StaticContext.init(..)}.
+ * This allows using the temp folder w/o the need for <code>sdcard</code>
+ * access, which would be the <code>java.io.tempdir</code> location on Android!
+ * </p>
+ * <p>
+ * In case {@code temp-root} is the users home folder,
+ * a dot is being prepended to {@link #tmpSubDir}, i.e.: {@code /home/user/.jogamp_0000/}.
+ * </p>
+ * @param executable true if the user intents to launch executables from the temporary directory, otherwise false.
+ * @throws RuntimeException if no temporary directory could be determined
+ * @throws SecurityException if access to <code>java.io.tmpdir</code> is not allowed within the current security context
+ *
+ * @see PropertyAccess#getProperty(String, boolean)
+ * @see Context#getDir(String, int)
+ */
+ public static File getTempDir(final boolean executable)
throws SecurityException, RuntimeException
- {
+ {
if(!tempRootSet) { // volatile: ok
synchronized(IOUtil.class) {
if(!tempRootSet) {
@@ -889,8 +905,8 @@ public class IOUtil {
}
}
- final String java_io_tmpdir = PropertyAccess.getProperty(java_io_tmpdir_propkey, false, null);
- final String user_home = PropertyAccess.getProperty(user_home_propkey, false, null);
+ final String java_io_tmpdir = PropertyAccess.getProperty(java_io_tmpdir_propkey, false);
+ final String user_home = PropertyAccess.getProperty(user_home_propkey, false);
final String xdg_cache_home;
{
@@ -951,6 +967,8 @@ public class IOUtil {
if(null == r) {
throw new RuntimeException("Could not determine a temporary directory");
}
+ final FilePermission fp = new FilePermission(r.getAbsolutePath(), "read,write,delete");
+ SecurityUtil.checkPermission(fp);
return r;
}
private static File tempRootExec = null; // writeable and executable
@@ -958,53 +976,13 @@ public class IOUtil {
private static volatile boolean tempRootSet = false;
/**
- * Returns a platform independent writable directory for temporary files
- * consisting of the platform's {@code temp-root} + {@link #tmpSubDir},
- * e.g. {@code /tmp/jogamp_0000/}.
- * <p>
- * On standard Java the {@code temp-root} folder is specified by <code>java.io.tempdir</code>.
- * </p>
- * <p>
- * On Android the {@code temp-root} folder is relative to the applications local folder
- * (see {@link Context#getDir(String, int)}) is returned, if
- * the Android application/activity has registered it's Application Context
- * via {@link jogamp.common.os.android.StaticContext.StaticContext#init(Context, ClassLoader) StaticContext.init(..)}.
- * This allows using the temp folder w/o the need for <code>sdcard</code>
- * access, which would be the <code>java.io.tempdir</code> location on Android!
- * </p>
- * <p>
- * In case {@code temp-root} is the users home folder,
- * a dot is being prepended to {@link #tmpSubDir}, i.e.: {@code /home/user/.jogamp_0000/}.
- * </p>
- * @param executable true if the user intents to launch executables from the temporary directory, otherwise false.
- * @param acc The security {@link AccessControlContext} to access properties, environment vars, create directories and test <i>executability</i>
- * @throws SecurityException if access to <code>java.io.tmpdir</code> is not allowed within the current security context
- * @throws RuntimeException if no temporary directory could be determined
- *
- * @see PropertyAccess#getProperty(String, boolean, java.security.AccessControlContext)
- * @see Context#getDir(String, int)
- */
- public static File getTempDir(final boolean executable, AccessControlContext acc)
- throws SecurityException, RuntimeException
- {
- if( null != acc ) {
- return AccessController.doPrivileged(new PrivilegedAction<File>() {
- public File run() {
- return getTempDirImpl(executable);
- } }, acc);
- } else {
- return getTempDirImpl(executable);
- }
- }
-
- /**
* Utilizing {@link File#createTempFile(String, String, File)} using
- * {@link #getTempRoot(AccessControlContext, boolean)} as the directory parameter, ie. location
+ * {@link #getTempDir(boolean)} as the directory parameter, ie. location
* of the root temp folder.
*
* @see File#createTempFile(String, String)
* @see File#createTempFile(String, String, File)
- * @see #getTempRoot(AccessControlContext, boolean)
+ * @see #getTempDir(boolean)
*
* @param prefix
* @param suffix
@@ -1014,10 +992,10 @@ public class IOUtil {
* @throws IOException
* @throws SecurityException
*/
- public static File createTempFile(String prefix, String suffix, boolean executable, AccessControlContext acc)
+ public static File createTempFile(String prefix, String suffix, boolean executable)
throws IllegalArgumentException, IOException, SecurityException
{
- return File.createTempFile( prefix, suffix, getTempDir(executable, acc) );
+ return File.createTempFile( prefix, suffix, getTempDir(executable) );
}
public static void close(Closeable stream, boolean throwRuntimeException) throws RuntimeException {
diff --git a/src/java/com/jogamp/common/util/PropertyAccess.java b/src/java/com/jogamp/common/util/PropertyAccess.java
index 51b9533..dde6b50 100644
--- a/src/java/com/jogamp/common/util/PropertyAccess.java
+++ b/src/java/com/jogamp/common/util/PropertyAccess.java
@@ -48,12 +48,13 @@ public class PropertyAccess {
// 'jogamp.' and maybe other trusted prefixes will be added later via 'addTrustedPrefix()'
}
- public static final void addTrustedPrefix(String prefix, Class<?> certClass) {
- if(SecurityUtil.equalsLocalCert(certClass)) {
- trustedPrefixes.add(prefix);
- } else {
- throw new SecurityException("Illegal Access - prefix "+prefix+", with cert class "+certClass);
- }
+ /**
+ * @param prefix New prefix to be registered as trusted.
+ * @throws AccessControlException as thrown by {@link SecurityUtil#checkAllPermissions()}.
+ */
+ protected static final void addTrustedPrefix(String prefix) throws AccessControlException {
+ SecurityUtil.checkAllPermissions();
+ trustedPrefixes.add(prefix);
}
public static final boolean isTrusted(String propertyKey) {
@@ -65,11 +66,11 @@ public class PropertyAccess {
}
}
- /** @see #getProperty(String, boolean, AccessControlContext) */
- public static final int getIntProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc, int defaultValue) {
+ /** @see #getProperty(String, boolean) */
+ public static final int getIntProperty(final String property, final boolean jnlpAlias, int defaultValue) {
int i=defaultValue;
try {
- final String sv = PropertyAccess.getProperty(property, jnlpAlias, acc);
+ final String sv = PropertyAccess.getProperty(property, jnlpAlias);
if(null!=sv) {
i = Integer.valueOf(sv).intValue();
}
@@ -77,11 +78,11 @@ public class PropertyAccess {
return i;
}
- /** @see #getProperty(String, boolean, AccessControlContext) */
- public static final long getLongProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc, long defaultValue) {
+ /** @see #getProperty(String, boolean) */
+ public static final long getLongProperty(final String property, final boolean jnlpAlias, long defaultValue) {
long l=defaultValue;
try {
- final String sv = PropertyAccess.getProperty(property, jnlpAlias, acc);
+ final String sv = PropertyAccess.getProperty(property, jnlpAlias);
if(null!=sv) {
l = Long.valueOf(sv).longValue();
}
@@ -89,23 +90,23 @@ public class PropertyAccess {
return l;
}
- /** @see #getProperty(String, boolean, AccessControlContext) */
- public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc) {
- return Boolean.valueOf(PropertyAccess.getProperty(property, jnlpAlias, acc)).booleanValue();
+ /** @see #getProperty(String, boolean) */
+ public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias) {
+ return Boolean.valueOf(PropertyAccess.getProperty(property, jnlpAlias)).booleanValue();
}
- /** @see #getProperty(String, boolean, AccessControlContext) */
- public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc, boolean defaultValue) {
- final String valueS = PropertyAccess.getProperty(property, jnlpAlias, acc);
+ /** @see #getProperty(String, boolean) */
+ public static final boolean getBooleanProperty(final String property, final boolean jnlpAlias, boolean defaultValue) {
+ final String valueS = PropertyAccess.getProperty(property, jnlpAlias);
if(null != valueS) {
return Boolean.valueOf(valueS).booleanValue();
}
return defaultValue;
}
- /** @see #getProperty(String, boolean, AccessControlContext) */
- public static final boolean isPropertyDefined(final String property, final boolean jnlpAlias, final AccessControlContext acc) {
- return (PropertyAccess.getProperty(property, jnlpAlias, acc) != null) ? true : false;
+ /** @see #getProperty(String, boolean) */
+ public static final boolean isPropertyDefined(final String property, final boolean jnlpAlias) {
+ return (PropertyAccess.getProperty(property, jnlpAlias) != null) ? true : false;
}
/**
@@ -119,8 +120,6 @@ public class PropertyAccess {
* @param propertyKey the property name to query.
* @param jnlpAlias true if a fallback attempt to query the JNLP aliased <i>trusted property</i> shall be made,
* otherwise false.
- * @param acc the AccessControlerContext to be used for privileged access to the system property, or null.
- *
* @return the property value if exists, or null
*
* @throws NullPointerException if the property name is null
@@ -129,7 +128,7 @@ public class PropertyAccess {
*
* @see System#getProperty(String)
*/
- public static final String getProperty(final String propertyKey, final boolean jnlpAlias, final AccessControlContext acc)
+ public static final String getProperty(final String propertyKey, final boolean jnlpAlias)
throws SecurityException, NullPointerException, IllegalArgumentException {
if(null == propertyKey) {
throw new NullPointerException("propertyKey is NULL");
@@ -138,23 +137,13 @@ public class PropertyAccess {
throw new IllegalArgumentException("propertyKey is empty");
}
String s=null;
- // int cause = 0;
if( isTrusted(propertyKey) ) {
// 'trusted' property (jnlp., javaws., jogamp., ..)
s = getTrustedPropKey(propertyKey);
- // cause = null != s ? 1 : 0;
} else {
- if( null != acc ) {
- s = AccessController.doPrivileged(new PrivilegedAction<String>() {
- public String run() {
- return System.getProperty(propertyKey);
- } }, acc);
- // cause = null != s ? 2 : 0;
- } else {
- s = System.getProperty(propertyKey);
- // cause = null != s ? 3 : 0;
- }
+ // may throw SecurityException, AccessControlerException
+ s = System.getProperty(propertyKey);
}
if( null == s && jnlpAlias ) {
// Try 'jnlp.' aliased property ..
@@ -162,11 +151,8 @@ public class PropertyAccess {
// Properties within the namespace "jnlp." or "javaws." should be considered trusted,
// i.e. always granted w/o special privileges.
s = getTrustedPropKey(jnlp_prefix + propertyKey);
- // cause = null != s ? 4 : 0;
}
- }
- // System.err.println("Prop: <"+propertyKey+"> = <"+s+">, cause "+cause);
-
+ }
return s;
}
@@ -177,7 +163,6 @@ public class PropertyAccess {
return System.getProperty(propertyKey);
} catch (SecurityException se) {
throw new SecurityException("Could not access trusted property '"+propertyKey+"'", se);
-
}
}
});
diff --git a/src/java/com/jogamp/common/util/SecurityUtil.java b/src/java/com/jogamp/common/util/SecurityUtil.java
index 4583201..4d7aa5d 100644
--- a/src/java/com/jogamp/common/util/SecurityUtil.java
+++ b/src/java/com/jogamp/common/util/SecurityUtil.java
@@ -27,30 +27,127 @@
*/
package com.jogamp.common.util;
-import java.security.AccessControlContext;
import java.security.AccessController;
+import java.security.AllPermission;
import java.security.CodeSource;
+import java.security.Permission;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
public class SecurityUtil {
- /* package private */ static final AccessControlContext localACC;
- /* package private */ static final Certificate[] localCerts;
+ private static final SecurityManager securityManager;
+ private static final Permission allPermissions;
+ private static final boolean DEBUG = false;
static {
- localACC = AccessController.doPrivileged(new PrivilegedAction<AccessControlContext>() {
- public AccessControlContext run() {
- return AccessController.getContext();
- } } );
- localCerts = getCerts(SecurityUtil.class);
+ allPermissions = new AllPermission();
+ securityManager = System.getSecurityManager();
+
+ if( DEBUG ) {
+ final boolean hasAllPermissions;
+ {
+ final ProtectionDomain insecPD = AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() {
+ public ProtectionDomain run() {
+ return SecurityUtil.class.getProtectionDomain();
+ } } );
+ boolean _hasAllPermissions;
+ try {
+ insecPD.implies(allPermissions);
+ _hasAllPermissions = true;
+ } catch( SecurityException ace ) {
+ _hasAllPermissions = false;
+ }
+ hasAllPermissions = _hasAllPermissions;
+ }
+
+ System.err.println("SecurityUtil: Has SecurityManager: "+ ( null != securityManager ) ) ;
+ System.err.println("SecurityUtil: Has AllPermissions: "+hasAllPermissions);
+ final Certificate[] certs = AccessController.doPrivileged(new PrivilegedAction<Certificate[]>() {
+ public Certificate[] run() {
+ return getCerts(SecurityUtil.class);
+ } } );
+ System.err.println("SecurityUtil: Cert count: "+ ( null != certs ? certs.length : 0 ));
+ if( null != certs ) {
+ for(int i=0; i<certs.length; i++) {
+ System.err.println("\t cert["+i+"]: "+certs[i].toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if no {@link SecurityManager} has been installed
+ * or the installed {@link SecurityManager}'s <code>checkPermission(new AllPermission())</code>
+ * passes. Otherwise method returns <code>false</code>.
+ */
+ public static final boolean hasAllPermissions() {
+ return hasPermission(allPermissions);
+ }
+
+ /**
+ * Returns <code>true</code> if no {@link SecurityManager} has been installed
+ * or the installed {@link SecurityManager}'s <code>checkPermission(perm)</code>
+ * passes. Otherwise method returns <code>false</code>.
+ */
+ public static final boolean hasPermission(Permission perm) {
+ try {
+ checkPermission(perm);
+ return true;
+ } catch( SecurityException ace ) {
+ return false;
+ }
+ }
+
+ /**
+ * Throws an {@link SecurityException} if an installed {@link SecurityManager}
+ * does not permit the requested {@link AllPermission}.
+ */
+ public static final void checkAllPermissions() throws SecurityException {
+ checkPermission(allPermissions);
+ }
+
+ /**
+ * Throws an {@link SecurityException} if an installed {@link SecurityManager}
+ * does not permit the requested {@link Permission}.
+ */
+ public static final void checkPermission(Permission perm) throws SecurityException {
+ if( null != securityManager ) {
+ securityManager.checkPermission(perm);
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if no {@link SecurityManager} has been installed
+ * or the installed {@link SecurityManager}'s <code>checkLink(libName)</code>
+ * passes. Otherwise method returns <code>false</code>.
+ */
+ public static final boolean hasLinkPermission(String libName) {
+ try {
+ checkLinkPermission(libName);
+ return true;
+ } catch( SecurityException ace ) {
+ return false;
+ }
}
- public static final Certificate[] getCerts(final Class<?> clz) {
- final ProtectionDomain pd = AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() {
- public ProtectionDomain run() {
- return clz.getProtectionDomain();
- } } );
+ /**
+ * Throws an {@link SecurityException} if an installed {@link SecurityManager}
+ * does not permit to dynamically link the given libName.
+ */
+ public static final void checkLinkPermission(String libName) throws SecurityException {
+ if( null != securityManager ) {
+ securityManager.checkLink(libName);
+ }
+ }
+
+ /**
+ * @param clz
+ * @return
+ * @throws SecurityException if the caller has no permission to access the ProtectedDomain of the given class.
+ */
+ public static final Certificate[] getCerts(final Class<?> clz) throws SecurityException {
+ final ProtectionDomain pd = clz.getProtectionDomain();
final CodeSource cs = (null != pd) ? pd.getCodeSource() : null;
final Certificate[] certs = (null != cs) ? cs.getCertificates() : null;
return (null != certs && certs.length>0) ? certs : null;
@@ -72,21 +169,5 @@ public class SecurityUtil {
i++;
}
return i == a.length;
- }
-
- public static final boolean equalsLocalCert(Certificate[] b) {
- return equals(localCerts, b);
- }
-
- public static final boolean equalsLocalCert(Class<?> clz) {
- return equalsLocalCert(getCerts(clz));
- }
-
- public static final AccessControlContext getCommonAccessControlContext(Class<?> clz) {
- if(equalsLocalCert(clz)) {
- return localACC;
- } else {
- return null;
- }
- }
+ }
}
diff --git a/src/java/com/jogamp/common/util/cache/TempFileCache.java b/src/java/com/jogamp/common/util/cache/TempFileCache.java
index 755ed7b..32f5c2a 100644
--- a/src/java/com/jogamp/common/util/cache/TempFileCache.java
+++ b/src/java/com/jogamp/common/util/cache/TempFileCache.java
@@ -33,10 +33,8 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
-import java.security.AccessControlContext;
import com.jogamp.common.util.IOUtil;
-import com.jogamp.common.util.SecurityUtil;
import jogamp.common.Debug;
@@ -72,8 +70,6 @@ public class TempFileCache {
private File individualTmpDir;
static {
- final AccessControlContext acc = SecurityUtil.getCommonAccessControlContext(TempFileCache.class);
-
// Global Lock !
synchronized (System.out) {
// Create / initialize the temp root directory, starting the Reaper
@@ -81,8 +77,8 @@ public class TempFileCache {
// exception, set an error code.
File _tmpBaseDir = null;
try {
- _tmpBaseDir = new File(IOUtil.getTempDir(true /* executable */, acc), tmpDirPrefix);
- _tmpBaseDir = IOUtil.testDir(_tmpBaseDir, true /* create */, false /* executable */, acc); // executable already checked
+ _tmpBaseDir = new File(IOUtil.getTempDir(true /* executable */), tmpDirPrefix);
+ _tmpBaseDir = IOUtil.testDir(_tmpBaseDir, true /* create */, false /* executable */); // executable already checked
} catch (Exception ex) {
System.err.println("Warning: Catched Exception while retrieving temp base directory:");
ex.printStackTrace();