001 /*
002 * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.security.auth;
027
028 import java.security.AccessController;
029 import java.security.AccessControlContext;
030 import java.security.AllPermission;
031 import java.security.Permission;
032 import java.security.Permissions;
033 import java.security.PermissionCollection;
034 import java.security.Policy;
035 import java.security.Principal;
036 import java.security.PrivilegedAction;
037 import java.security.ProtectionDomain;
038 import java.lang.ClassLoader;
039 import java.security.Security;
040 import java.util.Set;
041 import java.util.Iterator;
042 import java.util.WeakHashMap;
043 import java.lang.ref.WeakReference;
044
045 /**
046 * A <code>SubjectDomainCombiner</code> updates ProtectionDomains
047 * with Principals from the <code>Subject</code> associated with this
048 * <code>SubjectDomainCombiner</code>.
049 *
050 * @version 1.57, 05/05/07
051 */
052 public class SubjectDomainCombiner implements
053 java.security.DomainCombiner {
054
055 private Subject subject;
056 private WeakKeyValueMap<ProtectionDomain, ProtectionDomain> cachedPDs = new WeakKeyValueMap<ProtectionDomain, ProtectionDomain>();
057 private Set<Principal> principalSet;
058 private Principal[] principals;
059
060 private static final sun.security.util.Debug debug = sun.security.util.Debug
061 .getInstance("combiner", "\t[SubjectDomainCombiner]");
062
063 // Note: check only at classloading time, not dynamically during combine()
064 private static final boolean useJavaxPolicy = compatPolicy();
065
066 // Relevant only when useJavaxPolicy is true
067 private static final boolean allowCaching = (useJavaxPolicy && cachePolicy());
068
069 /**
070 * Associate the provided <code>Subject</code> with this
071 * <code>SubjectDomainCombiner</code>.
072 *
073 * <p>
074 *
075 * @param subject the <code>Subject</code> to be associated with
076 * with this <code>SubjectDomainCombiner</code>.
077 */
078 public SubjectDomainCombiner(Subject subject) {
079 this .subject = subject;
080
081 if (subject.isReadOnly()) {
082 principalSet = subject.getPrincipals();
083 principals = (Principal[]) principalSet
084 .toArray(new Principal[principalSet.size()]);
085 }
086 }
087
088 /**
089 * Get the <code>Subject</code> associated with this
090 * <code>SubjectDomainCombiner</code>.
091 *
092 * <p>
093 *
094 * @return the <code>Subject</code> associated with this
095 * <code>SubjectDomainCombiner</code>, or <code>null</code>
096 * if no <code>Subject</code> is associated with this
097 * <code>SubjectDomainCombiner</code>.
098 *
099 * @exception SecurityException if the caller does not have permission
100 * to get the <code>Subject</code> associated with this
101 * <code>SubjectDomainCombiner</code>.
102 */
103 public Subject getSubject() {
104 java.lang.SecurityManager sm = System.getSecurityManager();
105 if (sm != null) {
106 sm.checkPermission(new AuthPermission(
107 "getSubjectFromDomainCombiner"));
108 }
109 return subject;
110 }
111
112 /**
113 * Update the relevant ProtectionDomains with the Principals
114 * from the <code>Subject</code> associated with this
115 * <code>SubjectDomainCombiner</code>.
116 *
117 * <p> A new <code>ProtectionDomain</code> instance is created
118 * for each <code>ProtectionDomain</code> in the
119 * <i>currentDomains</i> array. Each new <code>ProtectionDomain</code>
120 * instance is created using the <code>CodeSource</code>,
121 * <code>Permission</code>s and <code>ClassLoader</code>
122 * from the corresponding <code>ProtectionDomain</code> in
123 * <i>currentDomains</i>, as well as with the Principals from
124 * the <code>Subject</code> associated with this
125 * <code>SubjectDomainCombiner</code>.
126 *
127 * <p> All of the newly instantiated ProtectionDomains are
128 * combined into a new array. The ProtectionDomains from the
129 * <i>assignedDomains</i> array are appended to this new array,
130 * and the result is returned.
131 *
132 * <p> Note that optimizations such as the removal of duplicate
133 * ProtectionDomains may have occurred.
134 * In addition, caching of ProtectionDomains may be permitted.
135 *
136 * <p>
137 *
138 * @param currentDomains the ProtectionDomains associated with the
139 * current execution Thread, up to the most recent
140 * privileged <code>ProtectionDomain</code>.
141 * The ProtectionDomains are are listed in order of execution,
142 * with the most recently executing <code>ProtectionDomain</code>
143 * residing at the beginning of the array. This parameter may
144 * be <code>null</code> if the current execution Thread
145 * has no associated ProtectionDomains.<p>
146 *
147 * @param assignedDomains the ProtectionDomains inherited from the
148 * parent Thread, or the ProtectionDomains from the
149 * privileged <i>context</i>, if a call to
150 * AccessController.doPrivileged(..., <i>context</i>)
151 * had occurred This parameter may be <code>null</code>
152 * if there were no ProtectionDomains inherited from the
153 * parent Thread, or from the privileged <i>context</i>.
154 *
155 * @return a new array consisting of the updated ProtectionDomains,
156 * or <code>null</code>.
157 */
158 public ProtectionDomain[] combine(
159 ProtectionDomain[] currentDomains,
160 ProtectionDomain[] assignedDomains) {
161 if (debug != null) {
162 if (subject == null) {
163 debug.println("null subject");
164 } else {
165 final Subject s = subject;
166 AccessController
167 .doPrivileged(new java.security.PrivilegedAction<Void>() {
168 public Void run() {
169 debug.println(s.toString());
170 return null;
171 }
172 });
173 }
174 printInputDomains(currentDomains, assignedDomains);
175 }
176
177 if (currentDomains == null || currentDomains.length == 0) {
178 // No need to optimize assignedDomains because it should
179 // have been previously optimized (when it was set).
180
181 // Note that we are returning a direct reference
182 // to the input array - since ACC does not clone
183 // the arrays when it calls combiner.combine,
184 // multiple ACC instances may share the same
185 // array instance in this case
186
187 return assignedDomains;
188 }
189
190 // optimize currentDomains
191 //
192 // No need to optimize assignedDomains because it should
193 // have been previously optimized (when it was set).
194
195 currentDomains = optimize(currentDomains);
196 if (debug != null) {
197 debug.println("after optimize");
198 printInputDomains(currentDomains, assignedDomains);
199 }
200
201 if (currentDomains == null && assignedDomains == null) {
202 return null;
203 }
204
205 // maintain backwards compatibility for people who provide
206 // their own javax.security.auth.Policy implementations
207 if (useJavaxPolicy) {
208 return combineJavaxPolicy(currentDomains, assignedDomains);
209 }
210
211 int cLen = (currentDomains == null ? 0 : currentDomains.length);
212 int aLen = (assignedDomains == null ? 0
213 : assignedDomains.length);
214
215 // the ProtectionDomains for the new AccessControlContext
216 // that we will return
217 ProtectionDomain[] newDomains = new ProtectionDomain[cLen
218 + aLen];
219
220 boolean allNew = true;
221 synchronized (cachedPDs) {
222 if (!subject.isReadOnly()
223 && !subject.getPrincipals().equals(principalSet)) {
224
225 // if the Subject was mutated, clear the PD cache
226 Set<Principal> newSet = subject.getPrincipals();
227 synchronized (newSet) {
228 principalSet = new java.util.HashSet<Principal>(
229 newSet);
230 }
231 principals = (Principal[]) principalSet
232 .toArray(new Principal[principalSet.size()]);
233 cachedPDs.clear();
234
235 if (debug != null) {
236 debug.println("Subject mutated - clearing cache");
237 }
238 }
239
240 ProtectionDomain subjectPd;
241 for (int i = 0; i < cLen; i++) {
242 ProtectionDomain pd = currentDomains[i];
243
244 subjectPd = cachedPDs.getValue(pd);
245
246 if (subjectPd == null) {
247 subjectPd = new ProtectionDomain(
248 pd.getCodeSource(), pd.getPermissions(), pd
249 .getClassLoader(), principals);
250 cachedPDs.putValue(pd, subjectPd);
251 } else {
252 allNew = false;
253 }
254 newDomains[i] = subjectPd;
255 }
256 }
257
258 if (debug != null) {
259 debug.println("updated current: ");
260 for (int i = 0; i < cLen; i++) {
261 debug.println("\tupdated[" + i + "] = "
262 + printDomain(newDomains[i]));
263 }
264 }
265
266 // now add on the assigned domains
267 if (aLen > 0) {
268 System
269 .arraycopy(assignedDomains, 0, newDomains, cLen,
270 aLen);
271
272 // optimize the result (cached PDs might exist in assignedDomains)
273 if (!allNew) {
274 newDomains = optimize(newDomains);
275 }
276 }
277
278 // if aLen == 0 || allNew, no need to further optimize newDomains
279
280 if (debug != null) {
281 if (newDomains == null || newDomains.length == 0) {
282 debug.println("returning null");
283 } else {
284 debug.println("combinedDomains: ");
285 for (int i = 0; i < newDomains.length; i++) {
286 debug.println("newDomain " + i + ": "
287 + printDomain(newDomains[i]));
288 }
289 }
290 }
291
292 // return the new ProtectionDomains
293 if (newDomains == null || newDomains.length == 0) {
294 return null;
295 } else {
296 return newDomains;
297 }
298 }
299
300 /**
301 * Use the javax.security.auth.Policy implementation
302 */
303 private ProtectionDomain[] combineJavaxPolicy(
304 ProtectionDomain[] currentDomains,
305 ProtectionDomain[] assignedDomains) {
306
307 if (!allowCaching) {
308 java.security.AccessController
309 .doPrivileged(new PrivilegedAction<Void>() {
310 public Void run() {
311 // Call refresh only caching is disallowed
312 javax.security.auth.Policy.getPolicy()
313 .refresh();
314 return null;
315 }
316 });
317 }
318
319 int cLen = (currentDomains == null ? 0 : currentDomains.length);
320 int aLen = (assignedDomains == null ? 0
321 : assignedDomains.length);
322
323 // the ProtectionDomains for the new AccessControlContext
324 // that we will return
325 ProtectionDomain[] newDomains = new ProtectionDomain[cLen
326 + aLen];
327
328 synchronized (cachedPDs) {
329 if (!subject.isReadOnly()
330 && !subject.getPrincipals().equals(principalSet)) {
331
332 // if the Subject was mutated, clear the PD cache
333 Set<Principal> newSet = subject.getPrincipals();
334 synchronized (newSet) {
335 principalSet = new java.util.HashSet<Principal>(
336 newSet);
337 }
338 principals = (Principal[]) principalSet
339 .toArray(new Principal[principalSet.size()]);
340 cachedPDs.clear();
341
342 if (debug != null) {
343 debug.println("Subject mutated - clearing cache");
344 }
345 }
346
347 for (int i = 0; i < cLen; i++) {
348 ProtectionDomain pd = currentDomains[i];
349 ProtectionDomain subjectPd = cachedPDs.getValue(pd);
350
351 if (subjectPd == null) {
352
353 // XXX
354 // we must first add the original permissions.
355 // that way when we later add the new JAAS permissions,
356 // any unresolved JAAS-related permissions will
357 // automatically get resolved.
358
359 // get the original perms
360 Permissions perms = new Permissions();
361 PermissionCollection coll = pd.getPermissions();
362 java.util.Enumeration e;
363 if (coll != null) {
364 synchronized (coll) {
365 e = coll.elements();
366 while (e.hasMoreElements()) {
367 Permission newPerm = (Permission) e
368 .nextElement();
369 perms.add(newPerm);
370 }
371 }
372 }
373
374 // get perms from the policy
375
376 final java.security.CodeSource finalCs = pd
377 .getCodeSource();
378 final Subject finalS = subject;
379 PermissionCollection newPerms = java.security.AccessController
380 .doPrivileged(new PrivilegedAction<PermissionCollection>() {
381 public PermissionCollection run() {
382 return javax.security.auth.Policy
383 .getPolicy()
384 .getPermissions(finalS,
385 finalCs);
386 }
387 });
388
389 // add the newly granted perms,
390 // avoiding duplicates
391 synchronized (newPerms) {
392 e = newPerms.elements();
393 while (e.hasMoreElements()) {
394 Permission newPerm = (Permission) e
395 .nextElement();
396 if (!perms.implies(newPerm)) {
397 perms.add(newPerm);
398 if (debug != null)
399 debug.println("Adding perm "
400 + newPerm + "\n");
401 }
402 }
403 }
404 subjectPd = new ProtectionDomain(finalCs, perms, pd
405 .getClassLoader(), principals);
406
407 if (allowCaching)
408 cachedPDs.putValue(pd, subjectPd);
409 }
410 newDomains[i] = subjectPd;
411 }
412 }
413
414 if (debug != null) {
415 debug.println("updated current: ");
416 for (int i = 0; i < cLen; i++) {
417 debug
418 .println("\tupdated[" + i + "] = "
419 + newDomains[i]);
420 }
421 }
422
423 // now add on the assigned domains
424 if (aLen > 0) {
425 System
426 .arraycopy(assignedDomains, 0, newDomains, cLen,
427 aLen);
428 }
429
430 if (debug != null) {
431 if (newDomains == null || newDomains.length == 0) {
432 debug.println("returning null");
433 } else {
434 debug.println("combinedDomains: ");
435 for (int i = 0; i < newDomains.length; i++) {
436 debug.println("newDomain " + i + ": "
437 + newDomains[i].toString());
438 }
439 }
440 }
441
442 // return the new ProtectionDomains
443 if (newDomains == null || newDomains.length == 0) {
444 return null;
445 } else {
446 return newDomains;
447 }
448 }
449
450 private static ProtectionDomain[] optimize(
451 ProtectionDomain[] domains) {
452 if (domains == null || domains.length == 0)
453 return null;
454
455 ProtectionDomain[] optimized = new ProtectionDomain[domains.length];
456 ProtectionDomain pd;
457 int num = 0;
458 for (int i = 0; i < domains.length; i++) {
459
460 // skip domains with AllPermission
461 // XXX
462 //
463 // if (domains[i].implies(ALL_PERMISSION))
464 // continue;
465
466 // skip System Domains
467 if ((pd = domains[i]) != null) {
468
469 // remove duplicates
470 boolean found = false;
471 for (int j = 0; j < num && !found; j++) {
472 found = (optimized[j] == pd);
473 }
474 if (!found) {
475 optimized[num++] = pd;
476 }
477 }
478 }
479
480 // resize the array if necessary
481 if (num > 0 && num < domains.length) {
482 ProtectionDomain[] downSize = new ProtectionDomain[num];
483 System
484 .arraycopy(optimized, 0, downSize, 0,
485 downSize.length);
486 optimized = downSize;
487 }
488
489 return ((num == 0 || optimized.length == 0) ? null : optimized);
490 }
491
492 private static boolean cachePolicy() {
493 String s = AccessController
494 .doPrivileged(new PrivilegedAction<String>() {
495 public String run() {
496 return java.security.Security
497 .getProperty("cache.auth.policy");
498 }
499 });
500 if (s != null) {
501 return Boolean.parseBoolean(s);
502 }
503
504 // cache by default
505 return true;
506 }
507
508 // maintain backwards compatibility for people who provide
509 // their own javax.security.auth.Policy implementations
510 private static boolean compatPolicy() {
511 javax.security.auth.Policy javaxPolicy = AccessController
512 .doPrivileged(new PrivilegedAction<javax.security.auth.Policy>() {
513 public javax.security.auth.Policy run() {
514 return javax.security.auth.Policy.getPolicy();
515 }
516 });
517
518 if (!(javaxPolicy instanceof com.sun.security.auth.PolicyFile)) {
519 if (debug != null) {
520 debug.println("Providing backwards compatibility for "
521 + "javax.security.auth.policy implementation: "
522 + javaxPolicy.toString());
523 }
524
525 return true;
526 } else {
527 return false;
528 }
529 }
530
531 private static void printInputDomains(
532 ProtectionDomain[] currentDomains,
533 ProtectionDomain[] assignedDomains) {
534 if (currentDomains == null || currentDomains.length == 0) {
535 debug.println("currentDomains null or 0 length");
536 } else {
537 for (int i = 0; currentDomains != null
538 && i < currentDomains.length; i++) {
539 if (currentDomains[i] == null) {
540 debug.println("currentDomain " + i
541 + ": SystemDomain");
542 } else {
543 debug.println("currentDomain " + i + ": "
544 + printDomain(currentDomains[i]));
545 }
546 }
547 }
548
549 if (assignedDomains == null || assignedDomains.length == 0) {
550 debug.println("assignedDomains null or 0 length");
551 } else {
552 debug.println("assignedDomains = ");
553 for (int i = 0; assignedDomains != null
554 && i < assignedDomains.length; i++) {
555 if (assignedDomains[i] == null) {
556 debug.println("assignedDomain " + i
557 + ": SystemDomain");
558 } else {
559 debug.println("assignedDomain " + i + ": "
560 + printDomain(assignedDomains[i]));
561 }
562 }
563 }
564 }
565
566 private static String printDomain(final ProtectionDomain pd) {
567 if (pd == null) {
568 return "null";
569 }
570 return AccessController
571 .doPrivileged(new PrivilegedAction<String>() {
572 public String run() {
573 return pd.toString();
574 }
575 });
576 }
577
578 /**
579 * A HashMap that has weak keys and values.
580 *
581 * Key objects in this map are the "current" ProtectionDomain instances
582 * received via the combine method. Each "current" PD is mapped to a
583 * new PD instance that holds both the contents of the "current" PD,
584 * as well as the principals from the Subject associated with this combiner.
585 *
586 * The newly created "principal-based" PD values must be stored as
587 * WeakReferences since they contain strong references to the
588 * corresponding key object (the "current" non-principal-based PD),
589 * which will prevent the key from being GC'd. Specifically,
590 * a "principal-based" PD contains strong references to the CodeSource,
591 * signer certs, PermissionCollection and ClassLoader objects
592 * in the "current PD".
593 */
594 private static class WeakKeyValueMap<K, V> extends
595 WeakHashMap<K, WeakReference<V>> {
596
597 public V getValue(K key) {
598 WeakReference<V> wr = super .get(key);
599 if (wr != null) {
600 return wr.get();
601 }
602 return null;
603 }
604
605 public V putValue(K key, V value) {
606 WeakReference<V> wr = super .put(key, new WeakReference<V>(
607 value));
608 if (wr != null) {
609 return wr.get();
610 }
611 return null;
612 }
613 }
614 }
|