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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
@ -30,17 +30,16 @@ import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import com.sun.j2me.security.AccessController;
import com.sun.midp.lcdui.*;
import com.sun.midp.midlet.*;
import com.sun.midp.i18n.Resource;
import com.sun.midp.i18n.ResourceConstants;
import com.sun.midp.events.EventQueue;
import com.sun.midp.io.j2me.storage.*;
import com.sun.midp.configurator.Constants;
import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;
@ -50,14 +49,6 @@ import com.sun.midp.log.LogChannels;
* a MIDlet suite.
*/
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. */
private static SecurityToken classSecurityToken;
@ -65,34 +56,21 @@ public final class SecurityHandler {
/** The standard security exception message. */
public static final String STD_EX_MSG = "Application not authorized " +
"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
* 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 ApiPermissions for the token
* @param domain name of the security domain
*
* @exception SecurityException if caller is not permitted to call this
* method
*/
public SecurityHandler(byte[] apiPermissions, String domain) {
AccessController.checkPermission(Permissions.AMS_PERMISSION_NAME);
init(apiPermissions, domain);
public SecurityHandler(byte[] ApiPermissions, String domain) {
// No-op since we don't cache any permissions in Java
// Query native security manager every time
}
/**
@ -102,39 +80,16 @@ public final class SecurityHandler {
* the method for this instance of the VM.
*
* @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
*
* @exception SecurityException if caller is not permitted to call this
* method
*/
public SecurityHandler(SecurityToken securityToken,
byte[] apiPermissions, String domain) {
securityToken.checkIfPermissionAllowed(Permissions.AMS);
init(apiPermissions, domain);
}
/**
* 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);
byte[] ApiPermissions, String domain) {
// Do not cache anything
// Ask native security manager everytime
}
/**
@ -149,46 +104,23 @@ public final class SecurityHandler {
* -1 if the status is unknown
*/
public int checkPermission(String permission) {
int i;
int status = 0;
int permId;
synchronized (this) {
try {
i = Permissions.getId(permission);
} catch (SecurityException e) {
return 0; //not found, report denied
}
try {
permId = Permissions.getId(permission);
MIDletSuite current =
MIDletStateHandler.getMidletStateHandler().getMIDletSuite();
switch (permissions[i]) {
case Permissions.ALLOW:
case Permissions.BLANKET_GRANTED:
// report allowed
return 1;
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;
if (current != null) {
// query native security mgr for status
status = checkPermissionStatus0(current.getID(), permId);
}
} catch (SecurityException exc) {
// intentionally ignored
}
return status;
}
/**
@ -207,9 +139,7 @@ public final class SecurityHandler {
* the ID must be from
* {@link com.sun.midp.security.Permissions}
* @param title Resource constant for the title of the dialog
* @param question Resource constant for the question to ask the user
* @param oneshotQuestion Resource constant for the oneshot question to
* ask the user
* @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,
@ -217,9 +147,8 @@ public final class SecurityHandler {
* @param extraValue string to insert into a string,
* can be null if no %3 in a string
*
* @return <code>true</code> if the permission interaction has permanently
* changed and the new state should be saved, this will only happen
* if the permission granted
* @return true if the permission was allow and was not allowed
* before
*
* @exception SecurityException if the permission is not
* allowed by this token
@ -232,7 +161,8 @@ public final class SecurityHandler {
throws InterruptedException {
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
* @param exceptionMsg message if a security exception is thrown
*
* @return <code>true</code> if the permission interaction has permanently
* changed and the new state should be saved, this will only happen
* if the permission granted
* @return <code>true</code> if the permission was allowed and was
* not allowed before; <code>false</code>, if permission is granted..
*
* @exception SecurityException if the permission is not
* allowed by this token
@ -273,89 +202,21 @@ public final class SecurityHandler {
* calling thread while this method is waiting to preempt the
* 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 exceptionMsg) throws InterruptedException {
MIDletSuite current =
MIDletStateHandler.getMidletStateHandler().getMIDletSuite();
if (permissions == null) {
/* totally trusted, all permissions allowed */
return false;
if (current != null) {
// can throw SecurityException
int permId = Permissions.getId(permission);
if (checkPermission0(current.getID(), permId)) {
return false;
}
}
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
throw new SecurityException(STD_EX_MSG);
}
/**
@ -363,10 +224,10 @@ public final class SecurityHandler {
*
* @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 oneShotQuestion Resource constant for the oneshot question to
* ask the 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,
@ -383,12 +244,9 @@ public final class SecurityHandler {
public static boolean askUserForPermission(SecurityToken token,
boolean trusted, String title, String question, String app,
String resource, String extraValue) throws InterruptedException {
PermissionDialog dialog =
new PermissionDialog(token, trusted, title, question, app,
resource, extraValue);
return dialog.waitForAnswer();
// Allow Push interrupt since the decision is already made
// at native Push level
return true;
}
/**
@ -404,168 +262,33 @@ public final class SecurityHandler {
classSecurityToken = token;
}
}
/** Implements security permission dialog. */
class PermissionDialog implements CommandListener {
/** Caches the display manager reference. */
private DisplayEventHandler displayEventHandler;
/** Permission Alert. */
private Alert alert;
/** Command object for "Yes" command. */
private Command yesCmd =
new Command(Resource.getString(ResourceConstants.YES),
Command.OK, 1);
/** Command object for "No" command. */
private Command noCmd =
new Command(Resource.getString(ResourceConstants.NO),
Command.BACK, 1);
/** Holds the preempt token so the form can end. */
private Object preemptToken;
/** Holds the answer to the security question. */
private boolean answer;
/**
* Construct permission dialog.
* <p>
* The title, question, and answer strings will be translated,
* if a string resource is available.
* 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);
}
/**
* Query native security manager for permission.
* This call may block if user needs to be asked.
*
* @param suiteId the MIDlet suite the permission should be checked against
* @param permission the permission id
*
* @return true if permission is granted. Otherwise, false.
*/
private native boolean checkPermission0(int suiteId, int permission);
/**
* Get the status of the specified permission.
* This is to implement public API MIDlet.checkPermission()
* and will not block calling thread.
*
* If no API on the device defines the specific permission
* requested then it must be reported as denied.
* If the status of the permission is not known because it might
* require a user interaction then it should be reported as unknown.
*
* @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;
* -1 if the status is unknown
*/
private native int checkPermissionStatus0(int suiteId,
int permission);
}

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

@ -1140,3 +1140,13 @@ Native["com/ibm/oti/connection/file/FCOutputStream.writeImpl.([BIII)V"] = functi
_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":
value = "NOKIA503/JAVA_RUNTIME_VERSION=NOKIA_ASHA_1_2";
break;
case "microedition.profiles":
value = "MIDP-2.0"
break;
case "fileconn.dir.memorycard":
value = "fcfile:///";
break;