001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.geronimo.security;
019
020 import java.io.Serializable;
021 import java.security.AccessControlContext;
022 import java.security.AccessControlException;
023 import java.security.AccessController;
024 import java.security.InvalidKeyException;
025 import java.security.NoSuchAlgorithmException;
026 import java.security.Principal;
027 import java.security.PrivilegedAction;
028 import java.util.Hashtable;
029 import java.util.IdentityHashMap;
030 import java.util.Map;
031 import java.util.Set;
032 import javax.crypto.Mac;
033 import javax.crypto.SecretKey;
034 import javax.crypto.spec.SecretKeySpec;
035 import javax.security.auth.Subject;
036 import javax.security.jacc.EJBRoleRefPermission;
037
038 import org.apache.geronimo.security.realm.providers.GeronimoCallerPrincipal;
039
040
041 /**
042 * @version $Rev: 487175 $ $Date: 2006-12-14 03:10:31 -0800 (Thu, 14 Dec 2006) $
043 */
044 public class ContextManager {
045 private static ThreadLocal currentCallerId = new ThreadLocal();
046 private static final ThreadLocal callers = new ThreadLocal();
047 private static Map subjectContexts = new IdentityHashMap();
048 private static Map subjectIds = new Hashtable();
049 private static long nextSubjectId = System.currentTimeMillis();
050
051 private static SecretKey key;
052 private static String algorithm;
053 private static String password;
054
055 public static final GeronimoSecurityPermission GET_CONTEXT = new GeronimoSecurityPermission("getContext");
056 public static final GeronimoSecurityPermission SET_CONTEXT = new GeronimoSecurityPermission("setContext");
057
058 static {
059 password = "secret";
060 ContextManager.setAlgorithm("HmacSHA1");
061 }
062
063 /**
064 * After a login, the client is left with a relatively empty Subject, while
065 * the Subject used by the server has more important contents. This method
066 * lets a server-side component acting as an authentication client (such
067 * as Tocmat/Jetty) access the fully populated server-side Subject.
068 */
069 public static Subject getServerSideSubject(Subject clientSideSubject) {
070 Set set = clientSideSubject.getPrincipals(IdentificationPrincipal.class);
071 if(set == null || set.size() == 0) {
072 return null;
073 }
074 IdentificationPrincipal idp = (IdentificationPrincipal)set.iterator().next();
075 return getRegisteredSubject(idp.getId());
076 }
077
078 public static void setCurrentCallerId(Serializable id) {
079 SecurityManager sm = System.getSecurityManager();
080 if (sm != null) sm.checkPermission(SET_CONTEXT);
081
082 currentCallerId.set(id);
083 }
084
085 public static Serializable getCurrentCallerId() {
086 SecurityManager sm = System.getSecurityManager();
087 if (sm != null) sm.checkPermission(GET_CONTEXT);
088
089 return (Serializable) currentCallerId.get();
090 }
091
092 public static void setCallers(Subject currentCaller, Subject nextCaller) {
093 SecurityManager sm = System.getSecurityManager();
094 if (sm != null) sm.checkPermission(SET_CONTEXT);
095 assert currentCaller != null;
096 assert nextCaller != null;
097 Callers newCallers = new Callers(currentCaller, nextCaller);
098 callers.set(newCallers);
099 }
100
101 public static void clearCallers() {
102 callers.set(null);
103 }
104
105 public static Callers getCallers() {
106 SecurityManager sm = System.getSecurityManager();
107 if (sm != null) sm.checkPermission(GET_CONTEXT);
108 return (Callers) callers.get();
109 }
110
111 public static Callers setNextCaller(Subject nextCaller) {
112 SecurityManager sm = System.getSecurityManager();
113 if (sm != null) sm.checkPermission(SET_CONTEXT);
114 assert nextCaller != null;
115 Callers oldCallers = (Callers) callers.get();
116 assert oldCallers != null;
117 Callers newCallers = new Callers(oldCallers.getNextCaller(), nextCaller);
118 callers.set(newCallers);
119 return oldCallers;
120 }
121
122 public static Callers pushNextCaller(Subject nextCaller) {
123 SecurityManager sm = System.getSecurityManager();
124 if (sm != null) sm.checkPermission(SET_CONTEXT);
125 Callers oldCallers = (Callers) callers.get();
126 Subject oldNextCaller = oldCallers == null? null: oldCallers.getNextCaller();
127 Subject newNextCaller = nextCaller == null? oldNextCaller : nextCaller;
128 Callers newCallers = new Callers(oldNextCaller, newNextCaller);
129 callers.set(newCallers);
130 return oldCallers;
131 }
132
133 public static void popCallers(Callers oldCallers) {
134 SecurityManager sm = System.getSecurityManager();
135 if (sm != null) sm.checkPermission(SET_CONTEXT);
136 callers.set(oldCallers);
137 }
138
139 public static Subject getCurrentCaller() {
140 SecurityManager sm = System.getSecurityManager();
141 if (sm != null) sm.checkPermission(GET_CONTEXT);
142
143 Callers callers = (Callers) ContextManager.callers.get();
144 return callers == null? null: callers.getCurrentCaller();
145 }
146
147 public static Subject getNextCaller() {
148 SecurityManager sm = System.getSecurityManager();
149 if (sm != null) sm.checkPermission(GET_CONTEXT);
150
151 Callers callers = (Callers) ContextManager.callers.get();
152 return callers == null? null: callers.getNextCaller();
153 }
154
155 public static AccessControlContext getCurrentContext() {
156 SecurityManager sm = System.getSecurityManager();
157 if (sm != null) sm.checkPermission(GET_CONTEXT);
158
159 Callers threadLocalCallers = (Callers) callers.get();
160 assert threadLocalCallers != null : "No current callers";
161 Subject currentSubject = threadLocalCallers.getCurrentCaller();
162 assert currentSubject != null : "No current caller";
163 Context context = (Context) subjectContexts.get(currentSubject);
164
165 assert context != null : "No registered context";
166
167 return context.context;
168 }
169
170 public static Principal getCurrentPrincipal(Subject callerSubject) {
171 SecurityManager sm = System.getSecurityManager();
172 if (sm != null) sm.checkPermission(GET_CONTEXT);
173
174 if (callerSubject == null) {
175 return new Principal() {
176 public String getName() {
177 return "";
178 }
179 };
180 }
181 Context context = (Context) subjectContexts.get(callerSubject);
182
183 assert context != null : "No registered context";
184
185 return context.principal;
186 }
187
188 public static SubjectId getCurrentId() {
189 SecurityManager sm = System.getSecurityManager();
190 if (sm != null) sm.checkPermission(GET_CONTEXT);
191
192 Callers threadLocalCallers = (Callers) callers.get();
193 assert threadLocalCallers != null : "No current callers";
194 Subject currentSubject = threadLocalCallers.getCurrentCaller();
195 assert currentSubject != null : "No current caller";
196 Context context = (Context) subjectContexts.get(currentSubject);
197
198 assert context != null : "No registered context";
199
200 return context.id;
201 }
202
203 public static SubjectId getSubjectId(Subject subject) {
204 SecurityManager sm = System.getSecurityManager();
205 if (sm != null) sm.checkPermission(GET_CONTEXT);
206
207 Context context = (Context) subjectContexts.get(subject);
208
209 return (context != null ? context.id : null);
210 }
211
212 public static boolean isCallerInRole(String EJBName, String role) {
213 if (EJBName == null) throw new IllegalArgumentException("EJBName must not be null");
214 if (role == null) throw new IllegalArgumentException("Role must not be null");
215
216 try {
217 Callers currentCallers = (Callers)callers.get();
218 if (currentCallers == null) {
219 return false;
220 }
221 Subject currentSubject = currentCallers.getCurrentCaller();
222 if (currentSubject == null) {
223 return false;
224 }
225
226 Context context = (Context) subjectContexts.get(currentSubject);
227
228 assert context != null : "No registered context";
229
230 context.context.checkPermission(new EJBRoleRefPermission(EJBName, role));
231 } catch (AccessControlException e) {
232 return false;
233 }
234 return true;
235 }
236
237 public static Subject getRegisteredSubject(SubjectId id) {
238 return (Subject) subjectIds.get(id);
239 }
240
241 public static synchronized SubjectId registerSubject(Subject subject) {
242 SecurityManager sm = System.getSecurityManager();
243 if (sm != null) sm.checkPermission(SET_CONTEXT);
244
245 if (subject == null) throw new IllegalArgumentException("Subject must not be null");
246
247 AccessControlContext acc = (AccessControlContext) Subject.doAsPrivileged(subject, new PrivilegedAction() {
248 public Object run() {
249 return AccessController.getContext();
250 }
251 }, null);
252
253 Context context = new Context();
254 context.subject = subject;
255 context.context = acc;
256 Set principals = subject.getPrincipals(GeronimoCallerPrincipal.class);
257 if (!principals.isEmpty()) {
258 context.principal = (Principal) principals.iterator().next();
259 } else if (!(principals = subject.getPrincipals(PrimaryRealmPrincipal.class)).isEmpty()) {
260 context.principal = (PrimaryRealmPrincipal) principals.iterator().next();
261 } else if (!(principals = subject.getPrincipals(RealmPrincipal.class)).isEmpty()) {
262 context.principal = (RealmPrincipal) principals.iterator().next();
263 } else if (!(principals = subject.getPrincipals()).isEmpty()) {
264 context.principal = (Principal) principals.iterator().next();
265 }
266 Long id = new Long(nextSubjectId++);
267 context.id = new SubjectId(id, hash(id));
268
269 subjectIds.put(context.id, subject);
270 subjectContexts.put(subject, context);
271
272 return context.id;
273 }
274
275 public static synchronized void unregisterSubject(Subject subject) {
276 SecurityManager sm = System.getSecurityManager();
277 if (sm != null) sm.checkPermission(SET_CONTEXT);
278
279 if (subject == null) throw new IllegalArgumentException("Subject must not be null");
280
281 Context context = (Context) subjectContexts.get(subject);
282 if (context == null) return;
283
284 subjectIds.remove(context.id);
285 subjectContexts.remove(subject);
286 }
287
288 /**
289 * Obtain the thread's identifying principal.
290 * <p/>
291 * Clients should use <code>Subject.doAs*</code> to associate a Subject
292 * with the thread's call stack. It is this Subject that will be used for
293 * authentication checks.
294 * <p/>
295 * Return a <code>IdentificationPrincipal</code>. This kind of principal
296 * is inserted into a subject if one uses one of the Geronimo LoginModules.
297 * It is a secure id that identifies the Subject.
298 *
299 * @return the principal that identifies the Subject of this thread.
300 * @see Subject#doAs(javax.security.auth.Subject, java.security.PrivilegedAction)
301 * @see Subject#doAs(javax.security.auth.Subject, java.security.PrivilegedExceptionAction)
302 * @see Subject#doAsPrivileged(javax.security.auth.Subject, java.security.PrivilegedAction, java.security.AccessControlContext)
303 * @see Subject#doAsPrivileged(javax.security.auth.Subject, java.security.PrivilegedExceptionAction, java.security.AccessControlContext)
304 */
305 public static IdentificationPrincipal getThreadPrincipal() {
306 SecurityManager sm = System.getSecurityManager();
307 if (sm != null) sm.checkPermission(GET_CONTEXT);
308
309 Subject subject = Subject.getSubject(AccessController.getContext());
310 if (subject != null) {
311 Set set = subject.getPrincipals(IdentificationPrincipal.class);
312 if (!set.isEmpty()) return (IdentificationPrincipal) set.iterator().next();
313 }
314 return null;
315 }
316
317 public static String getAlgorithm() {
318 SecurityManager sm = System.getSecurityManager();
319 if (sm != null) sm.checkPermission(GET_CONTEXT);
320
321 return algorithm;
322 }
323
324 public static void setAlgorithm(String algorithm) {
325 SecurityManager sm = System.getSecurityManager();
326 if (sm != null) sm.checkPermission(SET_CONTEXT);
327
328 ContextManager.algorithm = algorithm;
329
330 key = new SecretKeySpec(password.getBytes(), algorithm);
331
332 /**
333 * Make sure that we can generate the Mac.
334 */
335 try {
336 Mac mac = Mac.getInstance(algorithm);
337 mac.init(key);
338 } catch (NoSuchAlgorithmException e) {
339 assert false : "Should never have reached here";
340 } catch (InvalidKeyException e) {
341 assert false : "Should never have reached here";
342 }
343 }
344
345 public static String getPassword() {
346 SecurityManager sm = System.getSecurityManager();
347 if (sm != null) sm.checkPermission(GET_CONTEXT);
348
349 return password;
350 }
351
352 public static void setPassword(String password) {
353 SecurityManager sm = System.getSecurityManager();
354 if (sm != null) sm.checkPermission(SET_CONTEXT);
355
356 ContextManager.password = password;
357
358 key = new SecretKeySpec(password.getBytes(), algorithm);
359 }
360
361 private static byte[] hash(Long id) {
362 long n = id.longValue();
363 byte[] bytes = new byte[8];
364 for (int i = 7; i >= 0; i--) {
365 bytes[i] = (byte) (n);
366 n >>>= 8;
367 }
368
369 try {
370 Mac mac = Mac.getInstance(algorithm);
371 mac.init(key);
372 mac.update(bytes);
373
374 return mac.doFinal();
375 } catch (NoSuchAlgorithmException e) {
376 } catch (InvalidKeyException e) {
377 }
378 assert false : "Should never have reached here";
379 return null;
380 }
381
382 private static class Context {
383 SubjectId id;
384 AccessControlContext context;
385 Subject subject;
386 Principal principal;
387 }
388
389 }