blob: ea190c781ee1d03d490ad33c2fafa54a228e751f [file] [log] [blame]
Karl Pauls36407322008-03-07 00:37:30 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.framework.security.util;
20
21import java.io.File;
22import java.io.FilePermission;
23import java.lang.ref.ReferenceQueue;
24import java.lang.ref.SoftReference;
25import java.lang.ref.WeakReference;
Karl Pauls23287bd2010-01-10 22:11:27 +000026import java.security.AccessController;
Karl Pauls36407322008-03-07 00:37:30 +000027import java.security.AllPermission;
28import java.security.Permission;
29import java.security.PermissionCollection;
Karl Pauls23287bd2010-01-10 22:11:27 +000030import java.security.PrivilegedAction;
Karl Pauls978a7522010-04-14 19:32:29 +000031import java.util.Arrays;
Karl Pauls36407322008-03-07 00:37:30 +000032import java.util.Enumeration;
33import java.util.HashMap;
34import java.util.HashSet;
35import java.util.Iterator;
36import java.util.Map;
37
38import org.apache.felix.framework.util.SecureAction;
39import org.osgi.framework.AdminPermission;
40import org.osgi.framework.Bundle;
41import org.osgi.framework.BundleContext;
42import org.osgi.framework.InvalidSyntaxException;
43import org.osgi.framework.ServiceReference;
44import org.osgi.service.packageadmin.ExportedPackage;
45import org.osgi.service.packageadmin.PackageAdmin;
46import org.osgi.service.permissionadmin.PermissionInfo;
47
48/**
Karl Pauls23287bd2010-01-10 22:11:27 +000049 * A permission cache that uses permisssion infos as keys. Permission are
50 * created from the parent classloader or any exported package.
Karl Pauls36407322008-03-07 00:37:30 +000051 */
52// TODO: maybe use bundle events instead of soft/weak references
53public final class Permissions
54{
Karl Pauls23287bd2010-01-10 22:11:27 +000055 private static final ClassLoader m_classLoader = Permissions.class
56 .getClassLoader();
Karl Pauls36407322008-03-07 00:37:30 +000057
58 private static final Map m_permissionCache = new HashMap();
59 private static final Map m_permissions = new HashMap();
Karl Pauls23287bd2010-01-10 22:11:27 +000060 private static final ReferenceQueue m_permissionsQueue = new ReferenceQueue();
Karl Pauls36407322008-03-07 00:37:30 +000061
62 private static final ThreadLocal m_stack = new ThreadLocal();
63
64 private final Map m_cache;
65 private final ReferenceQueue m_queue;
66 private final BundleContext m_context;
67 private final PermissionInfo[] m_permissionInfos;
68 private final boolean m_allPermission;
69 private final SecureAction m_action;
70
71 public static final AllPermission ALL_PERMISSION = new AllPermission();
72
Karl Pauls23287bd2010-01-10 22:11:27 +000073 private static final PermissionInfo[] IMPLICIT = new PermissionInfo[] { new PermissionInfo(
74 FilePermission.class.getName(), "-", "read,write,delete") };
Karl Pauls36407322008-03-07 00:37:30 +000075
76 Permissions(PermissionInfo[] permissionInfos, BundleContext context,
77 SecureAction action)
78 {
79 m_context = context;
80 m_permissionInfos = permissionInfos;
81 m_cache = new HashMap();
82 m_queue = new ReferenceQueue();
83 m_action = action;
84 for (int i = 0; i < m_permissionInfos.length; i++)
85 {
86 if (m_permissionInfos[i].getType().equals(
87 AllPermission.class.getName()))
88 {
89 m_allPermission = true;
90 return;
91 }
92 }
93 m_allPermission = false;
94 }
95
96 public Permissions(BundleContext context, SecureAction action)
97 {
98 m_context = context;
99 m_permissionInfos = null;
100 m_cache = null;
101 m_queue = null;
102 m_allPermission = true;
103 m_action = action;
104 }
Karl Pauls23287bd2010-01-10 22:11:27 +0000105
Karl Pauls36407322008-03-07 00:37:30 +0000106 public PermissionInfo[] getImplicit(Bundle bundle)
107 {
Karl Paulsf3a9aea2010-12-22 18:36:01 +0000108 return new PermissionInfo[] {
109 IMPLICIT[0],
110 new PermissionInfo(AdminPermission.class.getName(), "(id="
111 + bundle.getBundleId() + ")", AdminPermission.METADATA),
112 new PermissionInfo(AdminPermission.class.getName(), "(id="
113 + bundle.getBundleId() + ")", AdminPermission.RESOURCE),
114 new PermissionInfo(AdminPermission.class.getName(), "(id="
115 + bundle.getBundleId() + ")", AdminPermission.CONTEXT) };
Karl Pauls36407322008-03-07 00:37:30 +0000116 }
117
118 public Permissions getPermissions(PermissionInfo[] permissionInfos)
119 {
120 cleanUp(m_permissionsQueue, m_permissions);
121
122 Permissions result = null;
123 synchronized (m_permissions)
124 {
Karl Pauls978a7522010-04-14 19:32:29 +0000125 result = (Permissions) m_permissions.get(new Entry(permissionInfos));
Karl Pauls36407322008-03-07 00:37:30 +0000126 }
127 if (result == null)
128 {
Karl Pauls978a7522010-04-14 19:32:29 +0000129 //permissionInfos may not be referenced by the new Permissions, as
130 //otherwise the reference in m_permissions prevents the key from
131 //being garbage collectable.
132 PermissionInfo[] permissionInfosClone = new PermissionInfo[permissionInfos.length];
133 System.arraycopy(permissionInfos, 0, permissionInfosClone, 0, permissionInfos.length);
134 result = new Permissions(permissionInfosClone, m_context, m_action);
Karl Pauls36407322008-03-07 00:37:30 +0000135 synchronized (m_permissions)
136 {
137 m_permissions.put(
138 new Entry(permissionInfos, m_permissionsQueue), result);
139 }
140 }
141 return result;
142 }
143
144 private static final class Entry extends WeakReference
145 {
146 private final int m_hashCode;
147
148 Entry(Object entry, ReferenceQueue queue)
149 {
150 super(entry, queue);
151 m_hashCode = entry.hashCode();
152 }
153
154 Entry(Object entry)
155 {
156 super(entry);
157 m_hashCode = entry.hashCode();
158 }
159
160 public Object get()
161 {
162 return super.get();
163 }
164
165 public int hashCode()
166 {
167 return m_hashCode;
168 }
169
170 public boolean equals(Object o)
171 {
172 if (o == null)
173 {
174 return false;
175 }
176
177 if (o == this)
178 {
179 return true;
180 }
181
182 Object entry = super.get();
183
Karl Pauls23287bd2010-01-10 22:11:27 +0000184 if (o instanceof Entry)
Karl Paulsd3b03742009-01-28 00:27:49 +0000185 {
Karl Pauls978a7522010-04-14 19:32:29 +0000186
187 Object otherEntry = ((Entry) o).get();
188 if (entry == null)
189 {
190 return otherEntry == null;
191 }
192 if (otherEntry == null)
193 {
194 return false;
195 }
196 if (!entry.getClass().equals(otherEntry.getClass()))
197 {
198 return false;
199 }
200 if (entry instanceof Object[])
201 {
202 return Arrays.equals((Object[])entry, (Object[])otherEntry);
203 }
Karl Pauls23287bd2010-01-10 22:11:27 +0000204 return entry.equals(((Entry) o).get());
205 }
206 else
Karl Paulsd3b03742009-01-28 00:27:49 +0000207 {
208 return false;
209 }
Karl Pauls36407322008-03-07 00:37:30 +0000210 }
211 }
212
213 private static final class DefaultPermissionCollection extends
214 PermissionCollection
215 {
216 private final Map m_perms = new HashMap();
217
218 public void add(Permission perm)
219 {
220 synchronized (m_perms)
221 {
222 m_perms.put(perm, perm);
223 }
224 }
225
226 public Enumeration elements()
227 {
228 throw new IllegalStateException("Not implemented");
229 }
230
231 public boolean implies(Permission perm)
232 {
Karl Paulsf3a9aea2010-12-22 18:36:01 +0000233 Map perms = null;
Karl Pauls36407322008-03-07 00:37:30 +0000234
235 synchronized (m_perms)
236 {
Karl Paulsf3a9aea2010-12-22 18:36:01 +0000237 perms = m_perms;
Karl Pauls36407322008-03-07 00:37:30 +0000238 }
239
Karl Paulsf3a9aea2010-12-22 18:36:01 +0000240 Permission permission = (Permission) perms.get(perm);
241
Karl Pauls36407322008-03-07 00:37:30 +0000242 if ((permission != null) && permission.implies(perm))
243 {
244 return true;
245 }
Karl Paulsf3a9aea2010-12-22 18:36:01 +0000246
247 for (Iterator iter = perms.values().iterator(); iter.hasNext();)
248 {
249 Permission current = (Permission) iter.next();
250 if ((current != null) && (current != permission)
251 && current.implies(perm))
Karl Pauls36407322008-03-07 00:37:30 +0000252 {
Karl Paulsf3a9aea2010-12-22 18:36:01 +0000253 return true;
Karl Pauls36407322008-03-07 00:37:30 +0000254 }
255 }
Karl Paulsf3a9aea2010-12-22 18:36:01 +0000256 return false;
257 }
Karl Pauls36407322008-03-07 00:37:30 +0000258 }
259
260 private void cleanUp(ReferenceQueue queue, Map cache)
261 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000262 for (Entry entry = (Entry) queue.poll(); entry != null; entry = (Entry) queue
263 .poll())
Karl Pauls36407322008-03-07 00:37:30 +0000264 {
265 synchronized (cache)
266 {
267 cache.remove(entry);
268 }
269 }
270 }
271
272 /**
Karl Pauls23287bd2010-01-10 22:11:27 +0000273 * @param target
274 * the permission to be implied
275 * @param bundle
276 * if not null then allow implicit permissions like file access
277 * to local data area
Karl Pauls36407322008-03-07 00:37:30 +0000278 * @return true if the permission is implied by this permissions object.
279 */
Karl Pauls23287bd2010-01-10 22:11:27 +0000280 public boolean implies(Permission target, final Bundle bundle)
Karl Pauls36407322008-03-07 00:37:30 +0000281 {
282 if (m_allPermission)
283 {
284 return true;
285 }
286
287 Class targetClass = target.getClass();
288
289 cleanUp(m_queue, m_cache);
290
291 if ((bundle != null) && targetClass == FilePermission.class)
292 {
293 for (int i = 0; i < m_permissionInfos.length; i++)
294 {
295 if (m_permissionInfos[i].getType().equals(
296 FilePermission.class.getName()))
297 {
298 String postfix = "";
299 String name = m_permissionInfos[i].getName();
Karl Pauls23287bd2010-01-10 22:11:27 +0000300 if (!"<<ALL FILES>>".equals(name))
Karl Pauls36407322008-03-07 00:37:30 +0000301 {
Karl Paulsdc1e7f82009-11-25 20:38:36 +0000302 if (name.endsWith("*") || name.endsWith("-"))
Karl Pauls36407322008-03-07 00:37:30 +0000303 {
Karl Paulsdc1e7f82009-11-25 20:38:36 +0000304 postfix = name.substring(name.length() - 1);
305 name = name.substring(0, name.length() - 1);
Karl Pauls36407322008-03-07 00:37:30 +0000306 }
Karl Paulsdc1e7f82009-11-25 20:38:36 +0000307 if (!(new File(name)).isAbsolute())
Karl Pauls36407322008-03-07 00:37:30 +0000308 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000309 BundleContext context = (BundleContext) AccessController
310 .doPrivileged(new PrivilegedAction()
311 {
312 public Object run()
313 {
314 return bundle.getBundleContext();
315 }
316 });
Karl Paulsdc1e7f82009-11-25 20:38:36 +0000317 if (context == null)
318 {
319 break;
320 }
Karl Pauls23287bd2010-01-10 22:11:27 +0000321 name = m_action.getAbsolutePath(new File(context
322 .getDataFile(""), name));
Karl Pauls36407322008-03-07 00:37:30 +0000323 }
Karl Paulsdc1e7f82009-11-25 20:38:36 +0000324 if (postfix.length() > 0)
Karl Pauls36407322008-03-07 00:37:30 +0000325 {
Karl Paulsdc1e7f82009-11-25 20:38:36 +0000326 if ((name.length() > 0) && !name.endsWith("/"))
327 {
328 name += "/" + postfix;
329 }
330 else
331 {
332 name += postfix;
333 }
Karl Pauls36407322008-03-07 00:37:30 +0000334 }
335 }
Karl Pauls23287bd2010-01-10 22:11:27 +0000336 Permission source = createPermission(new PermissionInfo(
337 FilePermission.class.getName(), name,
338 m_permissionInfos[i].getActions()), targetClass);
339 postfix = "";
340 name = target.getName();
341 if (!"<<ALL FILES>>".equals(name))
342 {
343 if (name.endsWith("*") || name.endsWith("-"))
344 {
345 postfix = name.substring(name.length() - 1);
346 name = name.substring(0, name.length() - 1);
347 }
348 if (!(new File(name)).isAbsolute())
349 {
350 BundleContext context = (BundleContext) AccessController
351 .doPrivileged(new PrivilegedAction()
352 {
353 public Object run()
354 {
355 return bundle.getBundleContext();
356 }
357 });
358 if (context == null)
359 {
360 break;
361 }
362 name = m_action.getAbsolutePath(new File(context
363 .getDataFile(""), name));
364 }
365 if (postfix.length() > 0)
366 {
367 if ((name.length() > 0) && !name.endsWith("/"))
368 {
369 name += "/" + postfix;
370 }
371 else
372 {
373 name += postfix;
374 }
375 }
376 }
377 Permission realTarget = createPermission(
Karl Pauls36407322008-03-07 00:37:30 +0000378 new PermissionInfo(FilePermission.class.getName(),
Karl Pauls23287bd2010-01-10 22:11:27 +0000379 name, target.getActions()), targetClass);
380 if (source.implies(realTarget))
381 {
382 return true;
383 }
Karl Pauls36407322008-03-07 00:37:30 +0000384 }
385 }
386 return false;
387 }
388
389 Object current = m_stack.get();
390
391 if (current == null)
392 {
393 m_stack.set(targetClass);
394 }
395 else
396 {
397 if (current instanceof HashSet)
398 {
399 if (((HashSet) current).contains(targetClass))
400 {
401 return false;
402 }
403 ((HashSet) current).add(targetClass);
404 }
405 else
406 {
407 if (current == targetClass)
408 {
409 return false;
410 }
411 HashSet frame = new HashSet();
412 frame.add(current);
413 frame.add(targetClass);
414 m_stack.set(frame);
415 current = frame;
416 }
417 }
418
419 try
420 {
421 SoftReference collectionEntry = null;
422
423 PermissionCollection collection = null;
424
425 synchronized (m_cache)
426 {
427 collectionEntry = (SoftReference) m_cache.get(targetClass);
428 }
429
430 if (collectionEntry != null)
431 {
432 collection = (PermissionCollection) collectionEntry.get();
433 }
434
435 if (collection == null)
436 {
437 collection = target.newPermissionCollection();
438
439 if (collection == null)
440 {
441 collection = new DefaultPermissionCollection();
442 }
443
444 for (int i = 0; i < m_permissionInfos.length; i++)
445 {
446 PermissionInfo permissionInfo = m_permissionInfos[i];
447 String infoType = permissionInfo.getType();
448 String permissionType = targetClass.getName();
449
450 if (infoType.equals(permissionType))
451 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000452 Permission permission = createPermission(
453 permissionInfo, targetClass);
Karl Pauls36407322008-03-07 00:37:30 +0000454
455 if (permission != null)
456 {
457 collection.add(permission);
458 }
459 }
460 }
461
462 synchronized (m_cache)
463 {
464 m_cache.put(new Entry(target.getClass(), m_queue),
465 new SoftReference(collection));
466 }
467 }
468
469 return collection.implies(target);
470 }
471 finally
472 {
473 if (current == null)
474 {
475 m_stack.set(null);
476 }
477 else
478 {
479 ((HashSet) current).remove(targetClass);
480 if (((HashSet) current).isEmpty())
481 {
482 m_stack.set(null);
483 }
484 }
485 }
486 }
487
488 private Permission addToCache(String encoded, Permission permission)
489 {
490 if (permission == null)
491 {
492 return null;
493 }
494
495 synchronized (m_permissionCache)
496 {
497 Map inner = null;
498
499 SoftReference ref = (SoftReference) m_permissionCache.get(encoded);
500 if (ref != null)
501 {
502 inner = (Map) ref.get();
503 }
504 if (inner == null)
505 {
506 inner = new HashMap();
507 m_permissionCache.put(encoded,
Karl Paulsd5b82882011-09-12 10:17:45 +0000508 new SoftReference(inner));
Karl Pauls36407322008-03-07 00:37:30 +0000509 }
510
511 inner.put(new Entry(permission.getClass()), new Entry(permission));
512 }
513
514 return permission;
515 }
516
517 private Permission getFromCache(String encoded, Class target)
518 {
519 synchronized (m_permissionCache)
520 {
521 SoftReference ref = (SoftReference) m_permissionCache.get(encoded);
522 if (ref != null)
523 {
524 Map inner = (Map) ref.get();
525 if (inner != null)
526 {
527 Entry entry = (Entry) inner.get(target);
528 if (entry != null)
529 {
530 Permission result = (Permission) entry.get();
531 if (result != null)
532 {
533 return result;
534 }
535 inner.remove(entry);
536 }
537 if (inner.isEmpty())
538 {
539 m_permissionCache.remove(encoded);
540 }
541 }
542 else
543 {
544 m_permissionCache.remove(encoded);
545 }
546 }
547
548 }
549
550 return null;
551 }
552
Karl Paulse0e4b292008-12-16 15:50:06 +0000553 private Permission createPermission(final PermissionInfo permissionInfo,
554 final Class target)
Karl Pauls36407322008-03-07 00:37:30 +0000555 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000556 return (Permission) AccessController
557 .doPrivileged(new PrivilegedAction()
Karl Paulse0e4b292008-12-16 15:50:06 +0000558 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000559 public Object run()
Karl Pauls36407322008-03-07 00:37:30 +0000560 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000561 Permission cached = getFromCache(permissionInfo
562 .getEncoded(), target);
Karl Pauls36407322008-03-07 00:37:30 +0000563
Karl Pauls23287bd2010-01-10 22:11:27 +0000564 if (cached != null)
565 {
566 return cached;
567 }
568
569 try
570 {
571 if (m_classLoader.loadClass(target.getName()) == target)
572 {
573 return addToCache(permissionInfo.getEncoded(),
574 createPermission(permissionInfo.getName(),
575 permissionInfo.getActions(), target));
576 }
577 }
578 catch (ClassNotFoundException e1)
579 {
580 }
581
582 ServiceReference[] refs = null;
583 try
584 {
585 refs = m_context.getServiceReferences(
586 PackageAdmin.class.getName(), null);
587 }
588 catch (InvalidSyntaxException e)
589 {
590 }
591 if (refs != null)
592 {
593 for (int i = 0; i < refs.length; i++)
594 {
595 PackageAdmin admin = (PackageAdmin) m_context
596 .getService(refs[i]);
597
598 if (admin != null)
Karl Pauls36407322008-03-07 00:37:30 +0000599 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000600 Permission result = null;
601 Bundle bundle = admin.getBundle(target);
602 if (bundle != null)
Karl Pauls36407322008-03-07 00:37:30 +0000603 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000604 ExportedPackage[] exports = admin
605 .getExportedPackages(bundle);
606 if (exports != null)
607 {
608 String name = target.getName();
609 name = name.substring(0, name
610 .lastIndexOf('.'));
611
612 for (int j = 0; j < exports.length; j++)
613 {
614 if (exports[j].getName().equals(
615 name))
616 {
617 result = createPermission(
618 permissionInfo.getName(),
619 permissionInfo.getActions(),
620 target);
621 break;
622 }
623 }
624 }
Karl Pauls36407322008-03-07 00:37:30 +0000625 }
Karl Pauls23287bd2010-01-10 22:11:27 +0000626
627 m_context.ungetService(refs[i]);
628
629 return addToCache(permissionInfo.getEncoded(),
630 result);
Karl Pauls36407322008-03-07 00:37:30 +0000631 }
632 }
633 }
634
Karl Pauls23287bd2010-01-10 22:11:27 +0000635 return null;
Karl Pauls36407322008-03-07 00:37:30 +0000636 }
Karl Pauls23287bd2010-01-10 22:11:27 +0000637 });
Karl Pauls36407322008-03-07 00:37:30 +0000638 }
639
640 private Permission createPermission(String name, String action, Class target)
641 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000642 // System.out.println("\n\n|" + name + "|\n--\n|" + action + "|\n--\n" +
643 // target + "\n\n");
Karl Pauls36407322008-03-07 00:37:30 +0000644 try
645 {
646 return (Permission) m_action.getConstructor(target,
647 new Class[] { String.class, String.class }).newInstance(
648 new Object[] { name, action });
649 }
650 catch (Exception ex)
651 {
Karl Pauls23287bd2010-01-10 22:11:27 +0000652 // TODO: log this or something
Karl Pauls36407322008-03-07 00:37:30 +0000653 }
654
655 return null;
656 }
657}