Merge pull request #56 from marco-c/native_security_handler

Use the native version of the SecurityHandler class
This commit is contained in:
Andreas Gal 2014-08-07 16:00:04 -07:00
Родитель 8a493d6ebd afda0c2b2a
Коммит f141e496a0
3 изменённых файлов: 90 добавлений и 354 удалений

Просмотреть файл

@ -1,5 +1,5 @@
/* /*
* *
* *
* Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
@ -30,17 +30,16 @@ import javax.microedition.io.*;
import javax.microedition.lcdui.*; import javax.microedition.lcdui.*;
import com.sun.j2me.security.AccessController;
import com.sun.midp.lcdui.*; import com.sun.midp.lcdui.*;
import com.sun.midp.midlet.*;
import com.sun.midp.i18n.Resource; import com.sun.midp.i18n.Resource;
import com.sun.midp.i18n.ResourceConstants; import com.sun.midp.i18n.ResourceConstants;
import com.sun.midp.events.EventQueue; import com.sun.midp.events.EventQueue;
import com.sun.midp.io.j2me.storage.*; import com.sun.midp.io.j2me.storage.*;
import com.sun.midp.configurator.Constants;
import com.sun.midp.log.Logging; import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels; import com.sun.midp.log.LogChannels;
@ -50,14 +49,6 @@ import com.sun.midp.log.LogChannels;
* a MIDlet suite. * a MIDlet suite.
*/ */
public final class SecurityHandler { public final class SecurityHandler {
/** Session level interaction has not occured. */
private final static byte NOT_ASKED = 0;
/** User granted permission for this session. */
private final static byte GRANTED = 1;
/** User denied permission for this session. */
private final static byte DENIED = -1;
/** The security token for this class. */ /** The security token for this class. */
private static SecurityToken classSecurityToken; private static SecurityToken classSecurityToken;
@ -65,34 +56,21 @@ public final class SecurityHandler {
/** The standard security exception message. */ /** The standard security exception message. */
public static final String STD_EX_MSG = "Application not authorized " + public static final String STD_EX_MSG = "Application not authorized " +
"to access the restricted API"; "to access the restricted API";
/** Permission list. */
private byte permissions[];
/** A flag for the session value of each permission. */
private byte sessionValues[];
/** Maximum permission level list. */
private byte maxPermissionLevels[];
/** True, if trusted. */
private boolean trusted;
/** /**
* Creates a security domain with a list of permitted actions or no list * Creates a security domain with a list of permitted actions or no list
* to indicate all actions. The caller must be have permission for * to indicate all actions. The caller must be have permission for
* <code>Permissions.MIDP</code> or be the first caller of * <code>Permissions.MIDP</code> or be the first caller of
* the method for this instance of the VM. * the method for this instance of the VM.
* *
* @param apiPermissions for the token * @param ApiPermissions for the token
* @param domain name of the security domain * @param domain name of the security domain
* *
* @exception SecurityException if caller is not permitted to call this * @exception SecurityException if caller is not permitted to call this
* method * method
*/ */
public SecurityHandler(byte[] apiPermissions, String domain) { public SecurityHandler(byte[] ApiPermissions, String domain) {
AccessController.checkPermission(Permissions.AMS_PERMISSION_NAME); // No-op since we don't cache any permissions in Java
init(apiPermissions, domain); // Query native security manager every time
} }
/** /**
@ -102,39 +80,16 @@ public final class SecurityHandler {
* the method for this instance of the VM. * the method for this instance of the VM.
* *
* @param securityToken security token of the caller * @param securityToken security token of the caller
* @param apiPermissions for the token, can be null * @param ApiPermissions for the token, can be null
* @param domain name of the security domain * @param domain name of the security domain
* *
* @exception SecurityException if caller is not permitted to call this * @exception SecurityException if caller is not permitted to call this
* method * method
*/ */
public SecurityHandler(SecurityToken securityToken, public SecurityHandler(SecurityToken securityToken,
byte[] apiPermissions, String domain) { byte[] ApiPermissions, String domain) {
securityToken.checkIfPermissionAllowed(Permissions.AMS); // Do not cache anything
init(apiPermissions, domain); // Ask native security manager everytime
}
/**
* Creates a security domain with a list of permitted actions or no list
* to indicate all actions. The caller must be have permission for
* <code>Permissions.MIDP</code> or be the first caller of
* the method for this instance of the VM.
*
* @param apiPermissions for the token
* @param domain name of the security domain
*
* @exception SecurityException if caller is not permitted to call this
* method
*/
private void init(byte[] apiPermissions, String domain) {
maxPermissionLevels =
(Permissions.forDomain(domain))[Permissions.MAX_LEVELS];
permissions = apiPermissions;
sessionValues = new byte[permissions.length];
trusted = Permissions.isTrusted(domain);
} }
/** /**
@ -149,46 +104,23 @@ public final class SecurityHandler {
* -1 if the status is unknown * -1 if the status is unknown
*/ */
public int checkPermission(String permission) { public int checkPermission(String permission) {
int i; int status = 0;
int permId;
synchronized (this) { try {
try { permId = Permissions.getId(permission);
i = Permissions.getId(permission); MIDletSuite current =
} catch (SecurityException e) { MIDletStateHandler.getMidletStateHandler().getMIDletSuite();
return 0; //not found, report denied
}
switch (permissions[i]) { if (current != null) {
case Permissions.ALLOW: // query native security mgr for status
case Permissions.BLANKET_GRANTED: status = checkPermissionStatus0(current.getID(), permId);
// report allowed }
return 1; } catch (SecurityException exc) {
// intentionally ignored
case Permissions.SESSION:
if (sessionValues[i] == GRANTED) {
// report allowed
return 1;
}
if (sessionValues[i] == DENIED) {
// report denied
return 0;
}
// fall through
case Permissions.BLANKET:
case Permissions.ONESHOT:
// report unknown
return -1;
default:
// Permissions.NEVER
break;
}
// report denied
return 0;
} }
return status;
} }
/** /**
@ -207,9 +139,7 @@ public final class SecurityHandler {
* the ID must be from * the ID must be from
* {@link com.sun.midp.security.Permissions} * {@link com.sun.midp.security.Permissions}
* @param title Resource constant for the title of the dialog * @param title Resource constant for the title of the dialog
* @param question Resource constant for the question to ask the user * @param question Resource constant for the question to ask user
* @param oneshotQuestion Resource constant for the oneshot question to
* ask the user
* @param app name of the application to insert into a string * @param app name of the application to insert into a string
* can be null if no %1 a string * can be null if no %1 a string
* @param resource string to insert into a string, * @param resource string to insert into a string,
@ -217,9 +147,8 @@ public final class SecurityHandler {
* @param extraValue string to insert into a string, * @param extraValue string to insert into a string,
* can be null if no %3 in a string * can be null if no %3 in a string
* *
* @return <code>true</code> if the permission interaction has permanently * @return true if the permission was allow and was not allowed
* changed and the new state should be saved, this will only happen * before
* if the permission granted
* *
* @exception SecurityException if the permission is not * @exception SecurityException if the permission is not
* allowed by this token * allowed by this token
@ -232,7 +161,8 @@ public final class SecurityHandler {
throws InterruptedException { throws InterruptedException {
return checkForPermission(permission, title, question, return checkForPermission(permission, title, question,
oneshotQuestion, app, resource, extraValue, STD_EX_MSG); oneshotQuestion, app, resource, extraValue,
SecurityToken.STD_EX_MSG);
} }
@ -263,9 +193,8 @@ public final class SecurityHandler {
* can be null if no %3 in a string * can be null if no %3 in a string
* @param exceptionMsg message if a security exception is thrown * @param exceptionMsg message if a security exception is thrown
* *
* @return <code>true</code> if the permission interaction has permanently * @return <code>true</code> if the permission was allowed and was
* changed and the new state should be saved, this will only happen * not allowed before; <code>false</code>, if permission is granted..
* if the permission granted
* *
* @exception SecurityException if the permission is not * @exception SecurityException if the permission is not
* allowed by this token * allowed by this token
@ -273,89 +202,21 @@ public final class SecurityHandler {
* calling thread while this method is waiting to preempt the * calling thread while this method is waiting to preempt the
* display. * display.
*/ */
public boolean checkForPermission(String permissionStr, String title, String question, public boolean checkForPermission(String permission, String title, String question,
String oneShotQuestion, String app, String resource, String extraValue, String oneShotQuestion, String app, String resource, String extraValue,
String exceptionMsg) throws InterruptedException { String exceptionMsg) throws InterruptedException {
MIDletSuite current =
MIDletStateHandler.getMidletStateHandler().getMIDletSuite();
if (permissions == null) { if (current != null) {
/* totally trusted, all permissions allowed */ // can throw SecurityException
return false; int permId = Permissions.getId(permission);
if (checkPermission0(current.getID(), permId)) {
return false;
}
} }
throw new SecurityException(STD_EX_MSG);
synchronized (this) {
int permission;
try {
permission = Permissions.getId(permissionStr);
} catch (SecurityException e) {
throw new SecurityException(exceptionMsg);
}
if (permission >= 0 && permission < permissions.length) {
switch (permissions[permission]) {
case Permissions.ALLOW:
case Permissions.BLANKET_GRANTED:
return false;
case Permissions.BLANKET:
/* This level means the question has not been asked yet. */
if (askUserForPermission(classSecurityToken, trusted,
title, question, app, resource, extraValue)) {
Permissions.setPermissionGroup(permissions,
permission, Permissions.BLANKET_GRANTED);
return true;
}
Permissions.setPermissionGroup(permissions,
permission, Permissions.BLANKET_DENIED);
break;
case Permissions.SESSION:
if (sessionValues[permission] == GRANTED) {
return false;
}
if (sessionValues[permission] == DENIED) {
break;
}
if (askUserForPermission(classSecurityToken, trusted,
title, question, app, resource, extraValue)) {
/*
* Save the fact that the question has already
* been asked this session.
*/
Permissions.setPermissionGroup(sessionValues,
permission, GRANTED);
return false;
}
/*
* Save the fact that the question has already
* been asked this session.
*/
Permissions.setPermissionGroup(sessionValues,
permission, DENIED);
break;
case Permissions.ONESHOT:
if (askUserForPermission(classSecurityToken, trusted,
title, oneShotQuestion, app, resource,
extraValue)) {
return false;
}
break;
default:
// Permissions.NEVER
break;
} // switch
} // if
throw new SecurityException(exceptionMsg);
} // synchronized
} }
/** /**
@ -363,10 +224,10 @@ public final class SecurityHandler {
* *
* @param token security token with the permission to preempt the * @param token security token with the permission to preempt the
* foreground display * foreground display
* @param trusted true to display the trusted icon, false to display the
* untrusted icon
* @param title Resource constant for the title of the dialog * @param title Resource constant for the title of the dialog
* @param question Resource constant for the question to ask user * @param question Resource constant for the question to ask user
* @param oneShotQuestion Resource constant for the oneshot question to
* ask the user
* @param app name of the application to insert into a string * @param app name of the application to insert into a string
* can be null if no %1 a string * can be null if no %1 a string
* @param resource string to insert into a string, * @param resource string to insert into a string,
@ -383,12 +244,9 @@ public final class SecurityHandler {
public static boolean askUserForPermission(SecurityToken token, public static boolean askUserForPermission(SecurityToken token,
boolean trusted, String title, String question, String app, boolean trusted, String title, String question, String app,
String resource, String extraValue) throws InterruptedException { String resource, String extraValue) throws InterruptedException {
// Allow Push interrupt since the decision is already made
PermissionDialog dialog = // at native Push level
new PermissionDialog(token, trusted, title, question, app, return true;
resource, extraValue);
return dialog.waitForAnswer();
} }
/** /**
@ -404,168 +262,33 @@ public final class SecurityHandler {
classSecurityToken = token; classSecurityToken = token;
} }
}
/**
/** Implements security permission dialog. */ * Query native security manager for permission.
class PermissionDialog implements CommandListener { * This call may block if user needs to be asked.
/** Caches the display manager reference. */ *
private DisplayEventHandler displayEventHandler; * @param suiteId the MIDlet suite the permission should be checked against
* @param permission the permission id
/** Permission Alert. */ *
private Alert alert; * @return true if permission is granted. Otherwise, false.
*/
/** Command object for "Yes" command. */ private native boolean checkPermission0(int suiteId, int permission);
private Command yesCmd =
new Command(Resource.getString(ResourceConstants.YES), /**
Command.OK, 1); * Get the status of the specified permission.
/** Command object for "No" command. */ * This is to implement public API MIDlet.checkPermission()
private Command noCmd = * and will not block calling thread.
new Command(Resource.getString(ResourceConstants.NO), *
Command.BACK, 1); * If no API on the device defines the specific permission
/** Holds the preempt token so the form can end. */ * requested then it must be reported as denied.
private Object preemptToken; * If the status of the permission is not known because it might
* require a user interaction then it should be reported as unknown.
/** Holds the answer to the security question. */ *
private boolean answer; * @param suiteId the MIDlet suite the permission should be checked against
* @param permission to check if denied, allowed, or unknown.
/** * @return 0 if the permission is denied; 1 if the permission is allowed;
* Construct permission dialog. * -1 if the status is unknown
* <p> */
* The title, question, and answer strings will be translated, private native int checkPermissionStatus0(int suiteId,
* if a string resource is available. int permission);
* Since the strings can have substitution token in them, if there is a
* "%" it must changed to "%%". If a string has a %1, the app parameter
* will be substituted for it. If a string has a "%2, the resource
* parameter will be substituted for it. If a string has a %3, the
* extraValue parameter will be substituted for it.
*
* @param token security token with the permission to preempt the
* foreground display
* @param trusted true to display the trusted icon, false to display the
* untrusted icon
* @param title Resource constant for the title of the dialog
* @param question Resource constant for the question to ask user
* @param app name of the application to insert into a string
* can be null if no %1 a string
* @param resource string to insert into a string,
* can be null if no %2 in a string
* @param extraValue string to insert into a string,
* can be null if no %3 in a string
*
* @exception InterruptedException if another thread interrupts the
* calling thread while this method is waiting to preempt the
* display.
*/
PermissionDialog(SecurityToken token, boolean trusted, String title,
String question, String app, String resource, String extraValue)
throws InterruptedException {
String[] substitutions = {app, resource, extraValue};
String iconFilename;
RandomAccessStream stream;
byte[] rawPng;
Image icon;
String configRoot = File.getConfigRoot(Constants.INTERNAL_STORAGE_ID);
alert = new Alert(Resource.getString(title, substitutions));
displayEventHandler =
DisplayEventHandlerFactory.getDisplayEventHandler(token);
if (trusted) {
iconFilename = configRoot + "trusted_icon.png";
} else {
iconFilename = configRoot + "untrusted_icon.png";
}
stream = new RandomAccessStream(token);
try {
stream.connect(iconFilename, Connector.READ);
rawPng = new byte[stream.getSizeOf()];
stream.readBytes(rawPng, 0, rawPng.length);
stream.disconnect();
icon = Image.createImage(rawPng, 0, rawPng.length);
alert.setImage(icon);
} catch (java.io.IOException noImage) {
}
alert.setString(Resource.getString(question, substitutions));
alert.addCommand(noCmd);
alert.addCommand(yesCmd);
alert.setCommandListener(this);
preemptToken = displayEventHandler.preemptDisplay(alert, true);
}
/**
* Waits for the user's answer.
*
* @return user's answer
*/
boolean waitForAnswer() {
synchronized (this) {
if (preemptToken == null) {
return false;
}
if (EventQueue.isDispatchThread()) {
// Developer programming error
throw new RuntimeException(
"Blocking call performed in the event thread");
}
try {
wait();
} catch (Throwable t) {
return false;
}
return answer;
}
}
/**
* Sets the user's answer and notifies waitForAnswer and
* ends the form.
*
* @param theAnswer user's answer
*/
private void setAnswer(boolean theAnswer) {
synchronized (this) {
answer = theAnswer;
/*
* Since this may be the only display, clear the alert,
* so the user will not be confused by alert text still
* displaying.
*
* The case should happen when running TCK test MIDlets in
* SVM mode.
*/
alert.setTitle(null);
alert.setString(null);
alert.setImage(null);
alert.addCommand(new Command("", 1, 1));
alert.removeCommand(noCmd);
alert.removeCommand(yesCmd);
displayEventHandler.donePreempting(preemptToken);
notify();
}
}
/**
* Respond to a command issued on security question form.
*
* @param c command activated by the user
* @param s the Displayable the command was on.
*/
public void commandAction(Command c, Displayable s) {
if (c == yesCmd) {
setAnswer(true);
return;
}
setAnswer(false);
}
} }

Просмотреть файл

@ -1140,3 +1140,13 @@ Native["com/ibm/oti/connection/file/FCOutputStream.writeImpl.([BIII)V"] = functi
_this.pos += count; _this.pos += count;
} }
Native["com/sun/midp/security/SecurityHandler.checkPermission0.(II)Z"] = function(ctx, stack) {
var permission = stack.pop(), suiteId = stack.pop(), _this = stack.pop();
stack.push(1);
}
Native["com/sun/midp/security/SecurityHandler.checkPermissionStatus0.(II)I"] = function(ctx, stack) {
var permission = stack.pop(), suiteId = stack.pop(), _this = stack.pop();
stack.push(1);
}

Просмотреть файл

@ -75,6 +75,9 @@ Native["java/lang/System.getProperty0.(Ljava/lang/String;)Ljava/lang/String;"] =
case "microedition.platform": case "microedition.platform":
value = "NOKIA503/JAVA_RUNTIME_VERSION=NOKIA_ASHA_1_2"; value = "NOKIA503/JAVA_RUNTIME_VERSION=NOKIA_ASHA_1_2";
break; break;
case "microedition.profiles":
value = "MIDP-2.0"
break;
case "fileconn.dir.memorycard": case "fileconn.dir.memorycard":
value = "fcfile:///"; value = "fcfile:///";
break; break;