Skip to content

Commit

Permalink
SecurityUtil: Generalize cert validation and AccessControlContext que…
Browse files Browse the repository at this point in the history
…ry; PropertyAccess: Fix security code, grant access to common 'trusted' properties

- SecurityUtil
  - Generalize cert validation for JAR and property access
  - Grant access to common AccessControlContext for 'same' cert

- PropertyAccess:
  - Fix security code: Passing the current AccessControlContext from the caller
    didn't include priviledges.

  - Grant access to common 'trusted' properties,
    which removes the need of passing the AccessControlContext for general properties
    like 'jnlp.', 'jogamp.' ..

  - Enable registering 'trusted' properties, when caller's cert is 'same'
  • Loading branch information
sgothel committed Mar 13, 2012
1 parent bab77b6 commit f4ac27e
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 142 deletions.
96 changes: 50 additions & 46 deletions src/java/com/jogamp/common/jvm/JNILibLoaderBase.java
Expand Up @@ -43,19 +43,19 @@
import java.lang.reflect.Method;
import java.net.URL;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.HashSet;

import com.jogamp.common.os.Platform;
import com.jogamp.common.util.JarUtil;
import com.jogamp.common.util.PropertyAccess;
import com.jogamp.common.util.SecurityUtil;
import com.jogamp.common.util.cache.TempJarCache;

import jogamp.common.Debug;
import jogamp.common.PropertyAccess;

public class JNILibLoaderBase {
public static final boolean DEBUG = Debug.debug("JNILibLoader");
private static final AccessControlContext localACC = AccessController.getContext();
public static final boolean DEBUG = Debug.debug("JNILibLoader");

public interface LoaderAction {
/**
Expand Down Expand Up @@ -230,53 +230,57 @@ protected static synchronized void loadLibrary(String libname, String[] preload,
static {
final String sunAppletLauncherProperty = "sun.jnlp.applet.launcher";
final String sunAppletLauncherClassName = "org.jdesktop.applet.util.JNLPAppletLauncher";
final boolean usingJNLPAppletLauncher = PropertyAccess.getBooleanProperty(sunAppletLauncherProperty, true, AccessController.getContext());

Class<?> launcherClass = null;
Method loadLibraryMethod = null;

if (usingJNLPAppletLauncher) {
try {
launcherClass = Class.forName(sunAppletLauncherClassName);
} catch (ClassNotFoundException cnfe) {
// oops .. look like JNLPAppletLauncher doesn't exist, despite property
// this may happen if a previous applet was using JNLPAppletLauncher in the same JVM
System.err.println("JNILibLoaderBase: <"+sunAppletLauncherClassName+"> not found, despite enabled property <"+sunAppletLauncherProperty+">, JNLPAppletLauncher was probably used before");
System.setProperty(sunAppletLauncherProperty, Boolean.FALSE.toString());
} catch (LinkageError le) {
throw le;
}
if(null != launcherClass) {
try {
loadLibraryMethod = launcherClass.getDeclaredMethod("loadLibrary", new Class[] { String.class });
} catch (NoSuchMethodException ex) {
if(DEBUG) {
ex.printStackTrace();
}
launcherClass = null;
}
}
}
final Method loadLibraryMethod = AccessController.doPrivileged(new PrivilegedAction<Method>() {
public Method run() {
final boolean usingJNLPAppletLauncher = Debug.getBooleanProperty(sunAppletLauncherProperty, true);

if(null==launcherClass) {
String launcherClassName = Debug.getProperty("jnlp.launcher.class", false, localACC);
if(null!=launcherClassName) {
try {
launcherClass = Class.forName(launcherClassName);
loadLibraryMethod = launcherClass.getDeclaredMethod("loadLibrary", new Class[] { String.class });
} catch (ClassNotFoundException ex) {
if(DEBUG) {
ex.printStackTrace();
Class<?> launcherClass = null;
Method loadLibraryMethod = null;

if (usingJNLPAppletLauncher) {
try {
launcherClass = Class.forName(sunAppletLauncherClassName);
} catch (ClassNotFoundException cnfe) {
// oops .. look like JNLPAppletLauncher doesn't exist, despite property
// this may happen if a previous applet was using JNLPAppletLauncher in the same JVM
System.err.println("JNILibLoaderBase: <"+sunAppletLauncherClassName+"> not found, despite enabled property <"+sunAppletLauncherProperty+">, JNLPAppletLauncher was probably used before");
System.setProperty(sunAppletLauncherProperty, Boolean.FALSE.toString());
} catch (LinkageError le) {
throw le;
}
if(null != launcherClass) {
try {
loadLibraryMethod = launcherClass.getDeclaredMethod("loadLibrary", new Class[] { String.class });
} catch (NoSuchMethodException ex) {
if(DEBUG) {
ex.printStackTrace();
}
launcherClass = null;
}
}
} catch (NoSuchMethodException ex) {
if(DEBUG) {
ex.printStackTrace();
}

if(null==launcherClass) {
String launcherClassName = PropertyAccess.getProperty("jnlp.launcher.class", false, null);
if(null!=launcherClassName) {
try {
launcherClass = Class.forName(launcherClassName);
loadLibraryMethod = launcherClass.getDeclaredMethod("loadLibrary", new Class[] { String.class });
} catch (ClassNotFoundException ex) {
if(DEBUG) {
ex.printStackTrace();
}
} catch (NoSuchMethodException ex) {
if(DEBUG) {
ex.printStackTrace();
}
launcherClass = null;
}
}
launcherClass = null;
}
}
}
// customLauncherClass = launcherClass;
return loadLibraryMethod;
} } );
customLoadLibraryMethod = loadLibraryMethod;
}

Expand Down
4 changes: 2 additions & 2 deletions src/java/com/jogamp/common/os/Platform.java
Expand Up @@ -41,7 +41,7 @@
import com.jogamp.common.util.VersionNumber;
import com.jogamp.common.util.cache.TempJarCache;

import jogamp.common.PropertyAccess;
import jogamp.common.Debug;
import jogamp.common.jvm.JVMUtil;
import jogamp.common.os.MachineDescriptionRuntime;

Expand Down Expand Up @@ -205,7 +205,7 @@ public enum CPUType {
os_and_arch = getOSAndArch(OS_TYPE, CPU_ARCH);

USE_TEMP_JAR_CACHE = (OS_TYPE != OSType.ANDROID) && isRunningFromJarURL() &&
PropertyAccess.getBooleanProperty(useTempJarCachePropName, true, AccessController.getContext(), true);
Debug.getBooleanProperty(useTempJarCachePropName, true, true);

loadGlueGenRTImpl();
JVMUtil.initSingleton(); // requires gluegen-rt, one-time init.
Expand Down
53 changes: 32 additions & 21 deletions src/java/com/jogamp/common/util/IOUtil.java
Expand Up @@ -36,15 +36,13 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;

import jogamp.common.Debug;
import jogamp.common.PropertyAccess;
import jogamp.common.os.android.StaticContext;

import android.content.Context;
Expand All @@ -55,7 +53,10 @@
import com.jogamp.common.os.Platform;

public class IOUtil {
private static final boolean DEBUG = Debug.isPropertyDefined("jogamp.debug.IOUtil", true, AccessController.getContext());
private static final boolean DEBUG = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil", true);

/** Std. temporary directory property key <code>java.io.tmpdir</code> */
public static final String java_io_tmpdir_propkey = "java.io.tmpdir";

private IOUtil() {}

Expand Down Expand Up @@ -475,12 +476,12 @@ public static boolean urlExists(URL url, String dbgmsg) {

/**
* Utilizing {@link File#createTempFile(String, String, File)} using
* {@link #getTempRoot()} as the directory parameter, ie. location
* {@link #getTempRoot(AccessControlContext)} as the directory parameter, ie. location
* of the root temp folder.
*
* @see File#createTempFile(String, String)
* @see File#createTempFile(String, String, File)
* @see #getTempRoot()
* @see #getTempRoot(AccessControlContext)
*
* @param prefix
* @param suffix
Expand All @@ -489,15 +490,18 @@ public static boolean urlExists(URL url, String dbgmsg) {
* @throws IOException
* @throws SecurityException
*/
public static File createTempFile(String prefix, String suffix)
public static File createTempFile(String prefix, String suffix, AccessControlContext acc)
throws IllegalArgumentException, IOException, SecurityException
{
return File.createTempFile( prefix, suffix, getTempRoot() );
{
return File.createTempFile( prefix, suffix, getTempRoot(acc) );
}

/**
* Returns a platform independent writable directory for temporary files.
* <p>
* On standard Java, the folder specified by <code>java.io.tempdir</code>
* is returned.
* </p>
* <p>
* On Android a <code>temp</code> folder relative to the applications local folder
* (see {@link Context#getDir(String, int)}) is returned, if
Expand All @@ -506,32 +510,39 @@ public static File createTempFile(String prefix, String suffix)
* 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>
* The purpose of this <code>wrapper</code> is to allow unique code to be used
* for both platforms w/o the need to handle extra permissions.
* </p>
*
* @throws SecurityException
* @throws RuntimeException
* @param acc The security {@link AccessControlContext} to access <code>java.io.tmpdir</code>
*
* @throws SecurityException if access to <code>java.io.tmpdir</code> is not allowed within the current security context
* @throws RuntimeException is the property <code>java.io.tmpdir</code> or the resulting temp directory is invalid
*
* @see PropertyAccess#getProperty(String, boolean, java.security.AccessControlContext)
* @see StaticContext#setContext(Context)
* @see Context#getDir(String, int)
*/
public static File getTempRoot()
public static File getTempRoot(AccessControlContext acc)
throws SecurityException, RuntimeException
{
if(AndroidVersion.isAvailable) {
final Context ctx = StaticContext.getContext();
if(null != ctx) {
final File tmpRoot = ctx.getDir("temp", Context.MODE_WORLD_READABLE);
if(null==tmpRoot|| !tmpRoot.isDirectory() || !tmpRoot.canWrite()) {
throw new RuntimeException("Not a writable directory: '"+tmpRoot+"', retrieved Android static context");
}
if(DEBUG) {
System.err.println("IOUtil.getTempRoot(Android): temp dir: "+tmpRoot.getAbsolutePath());
}
return tmpRoot;
}
}
final String tmpRootName = PropertyAccess.getProperty("java.io.tmpdir", false, AccessController.getContext());
final String tmpRootName = PropertyAccess.getProperty(java_io_tmpdir_propkey, false, acc);
if(null == tmpRootName || 0 == tmpRootName.length()) {
throw new RuntimeException("Property '"+java_io_tmpdir_propkey+"' value is empty: <"+tmpRootName+">");
}
final File tmpRoot = new File(tmpRootName);
if(null==tmpRoot || !tmpRoot.isDirectory() || !tmpRoot.canWrite()) {
throw new RuntimeException("Not a writable directory: '"+tmpRoot+"', retrieved by propery '"+java_io_tmpdir_propkey+"'");
}
if(DEBUG) {
System.err.println("IOUtil.getTempRoot(isAndroid: "+AndroidVersion.isAvailable+"): temp dir: "+tmpRoot.getAbsolutePath());
}
Expand All @@ -552,7 +563,7 @@ public static File getTempRoot()
* }
* }
* </pre>
* The <code>tempRootDir</code> is retrieved by {@link #getTempRoot()}.
* The <code>tempRootDir</code> is retrieved by {@link #getTempRoot(AccessControlContext)}.
* <p>
* The iteration through [000000-999999] ensures that the code is multi-user save.
* </p>
Expand All @@ -561,10 +572,10 @@ public static File getTempRoot()
* @throws IOException
* @throws SecurityException
*/
public static File getTempDir(String tmpDirPrefix)
public static File getTempDir(String tmpDirPrefix, AccessControlContext acc)
throws IOException, SecurityException
{
final File tempRoot = IOUtil.getTempRoot();
final File tempRoot = IOUtil.getTempRoot(acc);

for(int i = 0; i<=999999; i++) {
final String tmpDirSuffix = String.format("_%06d", i); // 6 digits for iteration
Expand Down
22 changes: 8 additions & 14 deletions src/java/com/jogamp/common/util/JarUtil.java
Expand Up @@ -539,26 +539,20 @@ private static final void validateCertificate(Certificate[] rootCerts,
// InputStream in order to be able to get its certificates

InputStream is = jar.getInputStream(entry);
while (is.read(buf) > 0) { }
is.close();
try {
while (is.read(buf) > 0) { }
} finally {
is.close();
}

// Get the certificates for the JAR entry
Certificate[] nativeCerts = entry.getCertificates();
final Certificate[] nativeCerts = entry.getCertificates();
if (nativeCerts == null || nativeCerts.length == 0) {
throw new SecurityException("no certificate for " + entry.getName() + " in " + jar.getName());
}

int checked = 0;
for (int i = 0; i < rootCerts.length; i++) {
for (int j = 0; j < nativeCerts.length; j++) {
if (nativeCerts[j].equals(rootCerts[i])){
checked++;
break;
}
}
}
if( checked != rootCerts.length ) {
throw new SecurityException("not all certificates match, only "+checked+" out of "+rootCerts.length+" for " + entry.getName() + " in " + jar.getName());
if( !SecurityUtil.equals(rootCerts, nativeCerts) ) {
throw new SecurityException("certificates not equal for " + entry.getName() + " in " + jar.getName());
}
}
}

0 comments on commit f4ac27e

Please sign in to comment.