blob: 34b895bcf7b3c16040430fa753e39106e45856f4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2021 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.permadmin;
import java.io.File;
import java.io.FilePermission;
import java.lang.reflect.Constructor;
import java.security.AccessController;
import java.security.AllPermission;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.osgi.service.permissionadmin.PermissionInfo;
public final class PermissionInfoCollection extends PermissionCollection {
private static final long serialVersionUID = 3140511562980923957L;
/* Used to find permission constructors in addPermissions */
static private final Class<?> twoStringClassArray[] = new Class[] {String.class, String.class};
static private final Class<?> oneStringClassArray[] = new Class[] {String.class};
static private final Class<?> noArgClassArray[] = new Class[] {};
static private final Class<?>[][] permClassArrayArgs = new Class[][] {noArgClassArray, oneStringClassArray, twoStringClassArray};
static private final String ALL_PERMISSION_NAME = AllPermission.class.getName();
static final String FILE_PERMISSION_NAME = FilePermission.class.getName();
static final String ALL_FILES = "<<ALL FILES>>"; //$NON-NLS-1$
/* @GuardedBy(cachedPermissionCollections) */
private final Map<Class<? extends Permission>, PermissionCollection> cachedPermissionCollections = new HashMap<>();
private final Map<BundlePermissions, PermissionCollection> cachedRelativeFilePermissionCollections;
private final boolean hasAllPermission;
private final PermissionInfo[] permInfos;
public PermissionInfoCollection(PermissionInfo[] permInfos) {
this.permInfos = permInfos;
boolean tempAllPermissions = false;
boolean allAbsolutePaths = true;
for (PermissionInfo info : permInfos) {
if (ALL_PERMISSION_NAME.equals(info.getType())) {
tempAllPermissions = true;
} else if (FILE_PERMISSION_NAME.equals(info.getType())) {
if (!(new File(info.getActions()).isAbsolute())) {
allAbsolutePaths = false;
}
}
}
this.hasAllPermission = tempAllPermissions;
this.cachedRelativeFilePermissionCollections = allAbsolutePaths ? null : new HashMap<>();
setReadOnly(); // collections are managed with ConditionalPermissionAdmin
}
@Override
public void add(Permission arg0) {
throw new SecurityException();
}
@Override
public Enumeration<Permission> elements() {
// TODO return an empty enumeration for now;
return BundlePermissions.EMPTY_ENUMERATION;
}
@Override
public boolean implies(Permission perm) {
return implies(null, perm);
}
boolean implies(final BundlePermissions bundlePermissions, Permission perm) {
if (hasAllPermission)
return true;
final Class<? extends Permission> permClass = perm.getClass();
PermissionCollection collection = getCachedCollection(bundlePermissions, permClass);
// must populate the collection outside of the lock to prevent class loader deadlock
if (collection == null) {
collection = perm.newPermissionCollection();
if (collection == null) {
collection = new PermissionsHash();
}
try {
final PermissionCollection targetCollection = collection;
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
addPermissions(bundlePermissions, targetCollection, permClass);
return null;
});
} catch (Exception e) {
if (e instanceof PrivilegedActionException) {
e = ((PrivilegedActionException) e).getException();
}
throw new SecurityException("Exception creating permissions: " + permClass + ": " + e.getMessage(), e); //$NON-NLS-1$ //$NON-NLS-2$
}
collection = cacheCollection(bundlePermissions, permClass, collection);
}
return collection.implies(perm);
}
PermissionCollection getCachedCollection(BundlePermissions bundlePermissions, Class<? extends Permission> permClass) {
synchronized (cachedPermissionCollections) {
if (bundlePermissions != null && cachedRelativeFilePermissionCollections != null && FILE_PERMISSION_NAME.equals(permClass.getName())) {
return cachedRelativeFilePermissionCollections.get(bundlePermissions);
}
return cachedPermissionCollections.get(permClass);
}
}
private PermissionCollection cacheCollection(BundlePermissions bundlePermissions, Class<? extends Permission> permClass, PermissionCollection collection) {
synchronized (cachedPermissionCollections) {
// check to see if another thread beat this thread at adding the collection
boolean relativeFiles = bundlePermissions != null && cachedRelativeFilePermissionCollections != null && FILE_PERMISSION_NAME.equals(permClass.getName());
PermissionCollection exists = relativeFiles ? cachedRelativeFilePermissionCollections.get(bundlePermissions) : cachedPermissionCollections.get(permClass);
if (exists != null) {
collection = exists;
} else {
if (relativeFiles) {
cachedRelativeFilePermissionCollections.put(bundlePermissions, collection);
} else {
cachedPermissionCollections.put(permClass, collection);
}
}
return collection;
}
}
PermissionInfo[] getPermissionInfos() {
return permInfos;
}
void addPermissions(BundlePermissions bundlePermissions, PermissionCollection collection, Class<? extends Permission> permClass) throws Exception {
String permClassName = permClass.getName();
Constructor<? extends Permission> constructor = null;
int numArgs = -1;
for (int i = permClassArrayArgs.length - 1; i >= 0; i--) {
try {
constructor = permClass.getConstructor(permClassArrayArgs[i]);
numArgs = i;
break;
} catch (NoSuchMethodException e) {
// ignore
}
}
if (constructor == null) {
throw new NoSuchMethodException(permClass.getName() + ".<init>()"); //$NON-NLS-1$
}
/*
* TODO: We need to cache the permission constructors to enhance performance (see bug 118813).
*/
for (PermissionInfo permInfo : permInfos) {
if (permInfo.getType().equals(permClassName)) {
String args[] = new String[numArgs];
if (numArgs > 0) {
args[0] = permInfo.getName();
}
if (numArgs > 1) {
args[1] = permInfo.getActions();
}
if (permInfo.getType().equals(FILE_PERMISSION_NAME)) {
// map FilePermissions for relative names to the bundle's data area
if (!args[0].equals(ALL_FILES)) {
File file = new File(args[0]);
if (!file.isAbsolute()) { // relative name
File target = bundlePermissions == null ? null : bundlePermissions.getBundle().getDataFile(permInfo.getName());
if (target == null) {
// ignore if we cannot find the data area
continue;
}
args[0] = target.getPath();
}
}
}
collection.add(constructor.newInstance((Object[]) args));
}
}
}
void clearPermissionCache() {
synchronized (cachedPermissionCollections) {
cachedPermissionCollections.clear();
if (cachedRelativeFilePermissionCollections != null) {
cachedRelativeFilePermissionCollections.clear();
}
}
}
}