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