blob: 1eb566095ad5ffdecb26df27b62fb10af5f213da [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;
26import java.security.AllPermission;
27import java.security.Permission;
28import java.security.PermissionCollection;
Karl Paulse0e4b292008-12-16 15:50:06 +000029import java.security.*;
Karl Pauls36407322008-03-07 00:37:30 +000030import java.util.Enumeration;
31import java.util.HashMap;
32import java.util.HashSet;
33import java.util.Iterator;
34import java.util.Map;
35
36import org.apache.felix.framework.util.SecureAction;
37import org.osgi.framework.AdminPermission;
38import org.osgi.framework.Bundle;
39import org.osgi.framework.BundleContext;
40import org.osgi.framework.InvalidSyntaxException;
41import org.osgi.framework.ServiceReference;
42import org.osgi.service.packageadmin.ExportedPackage;
43import org.osgi.service.packageadmin.PackageAdmin;
44import org.osgi.service.permissionadmin.PermissionInfo;
45
46/**
47 * A permission cache that uses permisssion infos as keys. Permission are created
48 * from the parent classloader or any exported package.
49 */
50// TODO: maybe use bundle events instead of soft/weak references
51public final class Permissions
52{
53 private static final ClassLoader m_classLoader =
54 Permissions.class.getClassLoader();
55
56 private static final Map m_permissionCache = new HashMap();
57 private static final Map m_permissions = new HashMap();
58 private static final ReferenceQueue m_permissionsQueue =
59 new ReferenceQueue();
60
61 private static final ThreadLocal m_stack = new ThreadLocal();
62
63 private final Map m_cache;
64 private final ReferenceQueue m_queue;
65 private final BundleContext m_context;
66 private final PermissionInfo[] m_permissionInfos;
67 private final boolean m_allPermission;
68 private final SecureAction m_action;
69
70 public static final AllPermission ALL_PERMISSION = new AllPermission();
71
72 private static final PermissionInfo[] IMPLICIT =
73 new PermissionInfo[] { new PermissionInfo(FilePermission.class
74 .getName(), "-", "read,write,delete") };
75
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 }
105
106 public PermissionInfo[] getImplicit(Bundle bundle)
107 {
108 return new PermissionInfo[] {
109 IMPLICIT[0],
110 new PermissionInfo(AdminPermission.class.getName(), "(id="
111 + bundle.getBundleId() + ")", AdminPermission.METADATA) };
112 }
113
114 public Permissions getPermissions(PermissionInfo[] permissionInfos)
115 {
116 cleanUp(m_permissionsQueue, m_permissions);
117
118 Permissions result = null;
119 synchronized (m_permissions)
120 {
121 result = (Permissions) m_permissions.get(permissionInfos);
122 }
123 if (result == null)
124 {
125 result = new Permissions(permissionInfos, m_context, m_action);
126 synchronized (m_permissions)
127 {
128 m_permissions.put(
129 new Entry(permissionInfos, m_permissionsQueue), result);
130 }
131 }
132 return result;
133 }
134
135 private static final class Entry extends WeakReference
136 {
137 private final int m_hashCode;
138
139 Entry(Object entry, ReferenceQueue queue)
140 {
141 super(entry, queue);
142 m_hashCode = entry.hashCode();
143 }
144
145 Entry(Object entry)
146 {
147 super(entry);
148 m_hashCode = entry.hashCode();
149 }
150
151 public Object get()
152 {
153 return super.get();
154 }
155
156 public int hashCode()
157 {
158 return m_hashCode;
159 }
160
161 public boolean equals(Object o)
162 {
163 if (o == null)
164 {
165 return false;
166 }
167
168 if (o == this)
169 {
170 return true;
171 }
172
173 Object entry = super.get();
174
175 if (entry == null)
176 {
177 return false;
178 }
179
180 return entry.equals(o);
181 }
182 }
183
184 private static final class DefaultPermissionCollection extends
185 PermissionCollection
186 {
187 private final Map m_perms = new HashMap();
188
189 public void add(Permission perm)
190 {
191 synchronized (m_perms)
192 {
193 m_perms.put(perm, perm);
194 }
195 }
196
197 public Enumeration elements()
198 {
199 throw new IllegalStateException("Not implemented");
200 }
201
202 public boolean implies(Permission perm)
203 {
204 Map perms = null;
205
206 synchronized (m_perms)
207 {
208 perms = m_perms;
209 }
210
211 Permission permission = (Permission) perms.get(perm);
212
213 if ((permission != null) && permission.implies(perm))
214 {
215 return true;
216 }
217
218 for (Iterator iter = perms.values().iterator(); iter.hasNext();)
219 {
220 Permission current = (Permission) iter.next();
221 if ((current != null) && (current != permission)
222 && current.implies(perm))
223 {
224 return true;
225 }
226 }
227 return false;
228 }
229 }
230
231 private void cleanUp(ReferenceQueue queue, Map cache)
232 {
233 for (Entry entry = (Entry) queue.poll(); entry != null; entry =
234 (Entry) queue.poll())
235 {
236 synchronized (cache)
237 {
238 cache.remove(entry);
239 }
240 }
241 }
242
243 /**
244 * @param target the permission to be implied
245 * @param bundle if not null then allow implicit permissions like file
246 * access to local data area
247 * @return true if the permission is implied by this permissions object.
248 */
249 public boolean implies(Permission target, Bundle bundle)
250 {
251 if (m_allPermission)
252 {
253 return true;
254 }
255
256 Class targetClass = target.getClass();
257
258 cleanUp(m_queue, m_cache);
259
260 if ((bundle != null) && targetClass == FilePermission.class)
261 {
262 for (int i = 0; i < m_permissionInfos.length; i++)
263 {
264 if (m_permissionInfos[i].getType().equals(
265 FilePermission.class.getName()))
266 {
267 String postfix = "";
268 String name = m_permissionInfos[i].getName();
269 if (name.endsWith("*") || name.endsWith("-"))
270 {
271 postfix = name.substring(name.length() - 1);
272 name = name.substring(0, name.length() - 1);
273 }
274 if (!(new File(name)).isAbsolute())
275 {
276 BundleContext context = bundle.getBundleContext();
277 if (context == null)
278 {
279 break;
280 }
281 name =
Karl Pauls35c1c342008-03-19 17:39:16 +0000282 m_action.getAbsolutePath(new File(context.getDataFile(""), name));
Karl Pauls36407322008-03-07 00:37:30 +0000283 }
284 if (postfix.length() > 0)
285 {
286 if ((name.length() > 0) && !name.endsWith("/"))
287 {
288 name += "/" + postfix;
289 }
290 else
291 {
292 name += postfix;
293 }
294 }
295 return createPermission(
296 new PermissionInfo(FilePermission.class.getName(),
297 name, m_permissionInfos[i].getActions()),
298 targetClass).implies(target);
299 }
300 }
301 return false;
302 }
303
304 Object current = m_stack.get();
305
306 if (current == null)
307 {
308 m_stack.set(targetClass);
309 }
310 else
311 {
312 if (current instanceof HashSet)
313 {
314 if (((HashSet) current).contains(targetClass))
315 {
316 return false;
317 }
318 ((HashSet) current).add(targetClass);
319 }
320 else
321 {
322 if (current == targetClass)
323 {
324 return false;
325 }
326 HashSet frame = new HashSet();
327 frame.add(current);
328 frame.add(targetClass);
329 m_stack.set(frame);
330 current = frame;
331 }
332 }
333
334 try
335 {
336 SoftReference collectionEntry = null;
337
338 PermissionCollection collection = null;
339
340 synchronized (m_cache)
341 {
342 collectionEntry = (SoftReference) m_cache.get(targetClass);
343 }
344
345 if (collectionEntry != null)
346 {
347 collection = (PermissionCollection) collectionEntry.get();
348 }
349
350 if (collection == null)
351 {
352 collection = target.newPermissionCollection();
353
354 if (collection == null)
355 {
356 collection = new DefaultPermissionCollection();
357 }
358
359 for (int i = 0; i < m_permissionInfos.length; i++)
360 {
361 PermissionInfo permissionInfo = m_permissionInfos[i];
362 String infoType = permissionInfo.getType();
363 String permissionType = targetClass.getName();
364
365 if (infoType.equals(permissionType))
366 {
367 Permission permission =
368 createPermission(permissionInfo, targetClass);
369
370 if (permission != null)
371 {
372 collection.add(permission);
373 }
374 }
375 }
376
377 synchronized (m_cache)
378 {
379 m_cache.put(new Entry(target.getClass(), m_queue),
380 new SoftReference(collection));
381 }
382 }
383
384 return collection.implies(target);
385 }
386 finally
387 {
388 if (current == null)
389 {
390 m_stack.set(null);
391 }
392 else
393 {
394 ((HashSet) current).remove(targetClass);
395 if (((HashSet) current).isEmpty())
396 {
397 m_stack.set(null);
398 }
399 }
400 }
401 }
402
403 private Permission addToCache(String encoded, Permission permission)
404 {
405 if (permission == null)
406 {
407 return null;
408 }
409
410 synchronized (m_permissionCache)
411 {
412 Map inner = null;
413
414 SoftReference ref = (SoftReference) m_permissionCache.get(encoded);
415 if (ref != null)
416 {
417 inner = (Map) ref.get();
418 }
419 if (inner == null)
420 {
421 inner = new HashMap();
422 m_permissionCache.put(encoded,
423 new SoftReference(inner, m_queue));
424 }
425
426 inner.put(new Entry(permission.getClass()), new Entry(permission));
427 }
428
429 return permission;
430 }
431
432 private Permission getFromCache(String encoded, Class target)
433 {
434 synchronized (m_permissionCache)
435 {
436 SoftReference ref = (SoftReference) m_permissionCache.get(encoded);
437 if (ref != null)
438 {
439 Map inner = (Map) ref.get();
440 if (inner != null)
441 {
442 Entry entry = (Entry) inner.get(target);
443 if (entry != null)
444 {
445 Permission result = (Permission) entry.get();
446 if (result != null)
447 {
448 return result;
449 }
450 inner.remove(entry);
451 }
452 if (inner.isEmpty())
453 {
454 m_permissionCache.remove(encoded);
455 }
456 }
457 else
458 {
459 m_permissionCache.remove(encoded);
460 }
461 }
462
463 }
464
465 return null;
466 }
467
Karl Paulse0e4b292008-12-16 15:50:06 +0000468 private Permission createPermission(final PermissionInfo permissionInfo,
469 final Class target)
Karl Pauls36407322008-03-07 00:37:30 +0000470 {
Karl Paulse0e4b292008-12-16 15:50:06 +0000471 return (Permission) AccessController.doPrivileged(new PrivilegedAction() {
472 public Object run()
473 {
Karl Pauls36407322008-03-07 00:37:30 +0000474 Permission cached = getFromCache(permissionInfo.getEncoded(), target);
475
476 if (cached != null)
477 {
478 return cached;
479 }
480
481 try
482 {
483 if (m_classLoader.loadClass(target.getName()) == target)
484 {
485 return addToCache(permissionInfo.getEncoded(),
486 createPermission(permissionInfo.getName(), permissionInfo
487 .getActions(), target));
488 }
489 }
490 catch (ClassNotFoundException e1)
491 {
492 }
493
494 ServiceReference[] refs = null;
495 try
496 {
497 refs =
498 m_context.getServiceReferences(PackageAdmin.class.getName(),
499 null);
500 }
501 catch (InvalidSyntaxException e)
502 {
503 }
504 if (refs != null)
505 {
506 for (int i = 0; i < refs.length; i++)
507 {
508 PackageAdmin admin =
509 (PackageAdmin) m_context.getService(refs[i]);
510
511 if (admin != null)
512 {
513 Permission result = null;
514 Bundle bundle = admin.getBundle(target);
515 if (bundle != null)
516 {
517 ExportedPackage[] exports =
518 admin.getExportedPackages(bundle);
519 if (exports != null)
520 {
521 String name = target.getName();
522 name = name.substring(0, name.lastIndexOf('.'));
523
524 for (int j = 0; j < exports.length; j++)
525 {
526 if (exports[j].getName().equals(name))
527 {
528 result =
529 createPermission(permissionInfo
530 .getName(), permissionInfo
531 .getActions(), target);
532 break;
533 }
534 }
535 }
536 }
537
538 m_context.ungetService(refs[i]);
539
540 return addToCache(permissionInfo.getEncoded(), result);
541 }
542 }
543 }
544
545 return null;
Karl Paulse0e4b292008-12-16 15:50:06 +0000546 }});
Karl Pauls36407322008-03-07 00:37:30 +0000547 }
548
549 private Permission createPermission(String name, String action, Class target)
550 {
551 try
552 {
553 return (Permission) m_action.getConstructor(target,
554 new Class[] { String.class, String.class }).newInstance(
555 new Object[] { name, action });
556 }
557 catch (Exception ex)
558 {
559 ex.printStackTrace();
560 }
561
562 return null;
563 }
564}