/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm;
import java.security.AccessControlException;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import org.jnode.annotation.CheckPermission;
import org.jnode.annotation.DoPrivileged;
import org.jnode.annotation.MagicPermission;
import org.jnode.vm.classmgr.VmMethod;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.scheduler.VmProcessor;
import org.jnode.vm.scheduler.VmThread;
/**
* JNode VM implementation of the java AccessControl system.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@MagicPermission
public final class VmAccessController {
/**
* Checks whether the access control context of the current thread allows the
* given Permission. Throws an <code>AccessControlException</code> when
* the permission is not allowed in the current context. Otherwise returns
* silently without throwing an exception.
*
* @param perm the permission to be checked.
* @throws AccessControlException thrown if the current context does not allow the given
* permission.
*/
@CheckPermission
public static void checkPermission(Permission perm)
throws AccessControlException {
if (!VmProcessor.current().isThreadSwitchActive()) {
// getContext().checkPermission(perm);
// This is an optimized version of
// getContext().checkPermission()
// that does not require any memory allocations.
final VmStackReader reader = VmProcessor.current()
.getArchitecture().getStackReader();
final VmStackFrameEnumerator sfEnum = new VmStackFrameEnumerator(reader);
int recursionCount = 0;
while (sfEnum.isValid()) {
final VmMethod method = sfEnum.getMethod();
if (method.hasDoPrivilegedPragma()) {
// Stop here with the current thread's stacktrace.
break;
} else if (method.hasCheckPermissionPragma()) {
// Be paranoia for now, let's check for recursive
// checkPermission calls.
recursionCount++;
if (recursionCount > 2) {
reader.debugStackTrace();
Unsafe.die("Recursive checkPermission");
}
} else {
final VmType<?> declClass = method.getDeclaringClass();
final ProtectionDomain pd = declClass.getProtectionDomain();
if (pd != null) {
// Unsafe.debug(":pd");
if (!pd.implies(perm)) {
// Unsafe.debug("Permission denied");
throw new AccessControlException("Permission \""
+ perm + "\" not granted due to "
+ declClass.getName());
}
}
}
if (method.hasPrivilegedActionPragma()) {
// Break here, do not include inherited thread context
return;
}
sfEnum.next();
}
final VmThread thread = VmThread.currentThread();
final VmAccessControlContext inheritedCtx = thread.getContext();
if (inheritedCtx != null) {
inheritedCtx.checkPermission(perm);
}
}
}
/**
* This method takes a "snapshot" of the current calling context, which
* includes the current Thread's inherited AccessControlContext, and places
* it in an AccessControlContext object. This context may then be checked at
* a later point, possibly in another thread.
*
* @return the AccessControlContext based on the current context.
*/
public static VmAccessControlContext getContext() {
final VmStackReader reader = VmProcessor.current()
.getArchitecture().getStackReader();
final VmStackFrame[] stack = reader.getVmStackTrace(VmMagic
.getCurrentFrame(), null, Integer.MAX_VALUE);
final int count = stack.length;
final ProtectionDomain domains[] = new ProtectionDomain[count];
for (int i = 0; i < count; i++) {
final VmMethod method = stack[i].getMethod();
if (method.hasDoPrivilegedPragma()) {
// Stop here
break;
} else if (method.hasPrivilegedActionPragma()) {
// Break here, do not include inherited thread context
return new VmAccessControlContext(domains, null);
} else {
domains[i] = method.getDeclaringClass().getProtectionDomain();
}
}
final VmThread thread = VmThread.currentThread();
return new VmAccessControlContext(domains, thread.getContext());
}
/**
* Calls the <code>run()</code> method of the given action with as
* (initial) access control context the given context combined with the
* protection domain of the calling class. Calls to
* <code>checkPermission()</code> in the <code>run()</code> method
* ignore all earlier protection domains of classes in the call chain, but
* add checks for the protection domains given in the supplied context.
*
* @param action the <code>PrivilegedAction</code> whose <code>run()</code>
* should be be called.
* @param context the <code>AccessControlContext</code> whose protection
* domains should be added to the protection domain of the
* calling class.
* @return the result of the <code>action.run()</code> method.
*/
@DoPrivileged
public static Object doPrivileged(PrivilegedAction action,
VmAccessControlContext context) {
final VmThread thread = VmThread.currentThread();
final VmAccessControlContext prevContext = thread.getContext();
thread.setContext(context);
try {
return action.run();
} finally {
thread.setContext(prevContext);
}
}
/**
* Calls the <code>run()</code> method of the given action with as
* (initial) access control context the given context combined with the
* protection domain of the calling class. Calls to
* <code>checkPermission()</code> in the <code>run()</code> method
* ignore all earlier protection domains of classes in the call chain, but
* add checks for the protection domains given in the supplied context. If
* the <code>run()</code> method throws an exception then this method will
* wrap that exception in an <code>PrivilegedActionException</code>.
*
* @param action the <code>PrivilegedExceptionAction</code> whose
* <code>run()</code> should be be called.
* @param context the <code>AccessControlContext</code> whose protection
* domains should be added to the protection domain of the
* calling class.
* @throws PrivilegedActionException wrapped around any exception that is thrown in the
* <code>run()</code> method.
* @return the result of the <code>action.run()</code> method.
*/
@DoPrivileged
public static Object doPrivileged(PrivilegedExceptionAction action,
VmAccessControlContext context) throws PrivilegedActionException {
final VmThread thread = VmThread.currentThread();
final VmAccessControlContext prevContext = thread.getContext();
thread.setContext(context);
try {
return action.run();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new PrivilegedActionException(e);
} finally {
thread.setContext(prevContext);
}
}
}