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 }