1
/*
2
 * $Id$
3
 *
4
 * Copyright (C) 2003-2009 JNode.org
5
 *
6
 * This library is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful, but 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
14
 * License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this library; If not, write to the Free Software Foundation, Inc., 
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
 */
20
 
21
package org.jnode.vm;
22
23
import java.security.AccessControlException;
24
import java.security.Permission;
25
import java.security.PrivilegedAction;
26
import java.security.PrivilegedActionException;
27
import java.security.PrivilegedExceptionAction;
28
import java.security.ProtectionDomain;
29
30
import org.jnode.annotation.CheckPermission;
31
import org.jnode.annotation.DoPrivileged;
32
import org.jnode.annotation.MagicPermission;
33
import org.jnode.vm.classmgr.VmMethod;
34
import org.jnode.vm.classmgr.VmType;
35
import org.jnode.vm.scheduler.VmProcessor;
36
import org.jnode.vm.scheduler.VmThread;
37
38
/**
39
 * JNode VM implementation of the java AccessControl system.
40
 *
41
 * @author Ewout Prangsma (epr@users.sourceforge.net)
42
 */
43
@MagicPermission
44
public final class VmAccessController {
45
46
    /**
47
     * Checks whether the access control context of the current thread allows the
48
     * given Permission. Throws an <code>AccessControlException</code> when
49
     * the permission is not allowed in the current context. Otherwise returns
50
     * silently without throwing an exception.
51
     *
52
     * @param perm the permission to be checked.
53
     * @throws AccessControlException thrown if the current context does not allow the given
54
     *                                permission.
55
     */
56
57
    @CheckPermission
58
    public static void checkPermission(Permission perm)
59
        throws AccessControlException {
60
        if (!VmProcessor.current().isThreadSwitchActive()) {
61
            // getContext().checkPermission(perm);
62
63
            // This is an optimized version of
64
            // getContext().checkPermission()
65
            // that does not require any memory allocations.
66
            final VmStackReader reader = VmProcessor.current()
67
                .getArchitecture().getStackReader();
68
            final VmStackFrameEnumerator sfEnum = new VmStackFrameEnumerator(reader);
69
            int recursionCount = 0;
70
            while (sfEnum.isValid()) {
71
                final VmMethod method = sfEnum.getMethod();
72
                if (method.hasDoPrivilegedPragma()) {
73
                    // Stop here with the current thread's stacktrace.
74
                    break;
75
                } else if (method.hasCheckPermissionPragma()) {
76
                    // Be paranoia for now, let's check for recursive
77
                    // checkPermission calls.
78
                    recursionCount++;
79
                    if (recursionCount > 2) {
80
                        reader.debugStackTrace();
81
                        Unsafe.die("Recursive checkPermission");
82
                    }
83
                } else {
84
                    final VmType<?> declClass = method.getDeclaringClass();
85
                    final ProtectionDomain pd = declClass.getProtectionDomain();
86
                    if (pd != null) {
87
                        // Unsafe.debug(":pd");
88
                        if (!pd.implies(perm)) {
89
                            // Unsafe.debug("Permission denied");
90
                            throw new AccessControlException("Permission \""
91
                                + perm + "\" not granted due to "
92
                                + declClass.getName());
93
                        }
94
                    }
95
                }
96
                if (method.hasPrivilegedActionPragma()) {
97
                    // Break here, do not include inherited thread context
98
                    return;
99
                }
100
                sfEnum.next();
101
            }
102
103
            final VmThread thread = VmThread.currentThread();
104
            final VmAccessControlContext inheritedCtx = thread.getContext();
105
            if (inheritedCtx != null) {
106
                inheritedCtx.checkPermission(perm);
107
            }
108
        }
109
    }
110
111
    /**
112
     * This method takes a "snapshot" of the current calling context, which
113
     * includes the current Thread's inherited AccessControlContext, and places
114
     * it in an AccessControlContext object. This context may then be checked at
115
     * a later point, possibly in another thread.
116
     *
117
     * @return the AccessControlContext based on the current context.
118
     */
119
    public static VmAccessControlContext getContext() {
120
        final VmStackReader reader = VmProcessor.current()
121
            .getArchitecture().getStackReader();
122
        final VmStackFrame[] stack = reader.getVmStackTrace(VmMagic
123
            .getCurrentFrame(), null, Integer.MAX_VALUE);
124
        final int count = stack.length;
125
        final ProtectionDomain domains[] = new ProtectionDomain[count];
126
127
        for (int i = 0; i < count; i++) {
128
            final VmMethod method = stack[i].getMethod();
129
            if (method.hasDoPrivilegedPragma()) {
130
                // Stop here
131
                break;
132
            } else if (method.hasPrivilegedActionPragma()) {
133
                // Break here, do not include inherited thread context
134
                return new VmAccessControlContext(domains, null);
135
            } else {
136
                domains[i] = method.getDeclaringClass().getProtectionDomain();
137
            }
138
        }
139
        final VmThread thread = VmThread.currentThread();
140
        return new VmAccessControlContext(domains, thread.getContext());
141
    }
142
143
    /**
144
     * Calls the <code>run()</code> method of the given action with as
145
     * (initial) access control context the given context combined with the
146
     * protection domain of the calling class. Calls to
147
     * <code>checkPermission()</code> in the <code>run()</code> method
148
     * ignore all earlier protection domains of classes in the call chain, but
149
     * add checks for the protection domains given in the supplied context.
150
     *
151
     * @param action  the <code>PrivilegedAction</code> whose <code>run()</code>
152
     *                should be be called.
153
     * @param context the <code>AccessControlContext</code> whose protection
154
     *                domains should be added to the protection domain of the
155
     *                calling class.
156
     * @return the result of the <code>action.run()</code> method.
157
     */
158
    @DoPrivileged
159
    public static Object doPrivileged(PrivilegedAction action,
160
                                      VmAccessControlContext context) {
161
        final VmThread thread = VmThread.currentThread();
162
        final VmAccessControlContext prevContext = thread.getContext();
163
        thread.setContext(context);
164
        try {
165
            return action.run();
166
        } finally {
167
            thread.setContext(prevContext);
168
        }
169
    }
170
171
    /**
172
     * Calls the <code>run()</code> method of the given action with as
173
     * (initial) access control context the given context combined with the
174
     * protection domain of the calling class. Calls to
175
     * <code>checkPermission()</code> in the <code>run()</code> method
176
     * ignore all earlier protection domains of classes in the call chain, but
177
     * add checks for the protection domains given in the supplied context. If
178
     * the <code>run()</code> method throws an exception then this method will
179
     * wrap that exception in an <code>PrivilegedActionException</code>.
180
     *
181
     * @param action  the <code>PrivilegedExceptionAction</code> whose
182
     *                <code>run()</code> should be be called.
183
     * @param context the <code>AccessControlContext</code> whose protection
184
     *                domains should be added to the protection domain of the
185
     *                calling class.
186
     * @throws PrivilegedActionException wrapped around any exception that is thrown in the
187
     *                                   <code>run()</code> method.
188
     * @return the result of the <code>action.run()</code> method.
189
     */
190
    @DoPrivileged
191
    public static Object doPrivileged(PrivilegedExceptionAction action,
192
                                      VmAccessControlContext context) throws PrivilegedActionException {
193
        final VmThread thread = VmThread.currentThread();
194
        final VmAccessControlContext prevContext = thread.getContext();
195
        thread.setContext(context);
196
        try {
197
            return action.run();
198
        } catch (RuntimeException e) {
199
            throw e;
200
        } catch (Exception e) {
201
            throw new PrivilegedActionException(e);
202
        } finally {
203
            thread.setContext(prevContext);
204
        }
205
    }
206
}