blob: 1126e03c798caeb7571fe63ec7f25f71ea16d067 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.permadmin;
import java.io.*;
import java.net.URL;
import java.security.*;
import java.util.*;
import org.eclipse.osgi.framework.adaptor.PermissionStorage;
import org.eclipse.osgi.framework.internal.core.*;
import org.eclipse.osgi.internal.signedcontent.DNChainMatching;
import org.osgi.framework.AdminPermission;
import org.osgi.framework.FrameworkEvent;
import org.osgi.service.condpermadmin.*;
import org.osgi.service.permissionadmin.PermissionAdmin;
import org.osgi.service.permissionadmin.PermissionInfo;
public final class SecurityAdmin implements PermissionAdmin, ConditionalPermissionAdmin {
private static final PermissionCollection DEFAULT_DEFAULT;
static {
AllPermission allPerm = new AllPermission();
DEFAULT_DEFAULT = allPerm.newPermissionCollection();
if (DEFAULT_DEFAULT != null)
DEFAULT_DEFAULT.add(allPerm);
}
private static final String ADMIN_IMPLIED_ACTIONS = AdminPermission.RESOURCE + ',' + AdminPermission.METADATA + ',' + AdminPermission.CLASS + ',' + AdminPermission.CONTEXT;
private static final PermissionInfo[] EMPTY_PERM_INFO = new PermissionInfo[0];
/* @GuardedBy(lock) */
private final PermissionAdminTable permAdminTable = new PermissionAdminTable();
/* @GuardedBy(lock) */
private SecurityTable condAdminTable;
/* @GuardedBy(lock) */
private PermissionInfoCollection permAdminDefaults;
/* @GuardedBy(lock) */
private long timeStamp = 0;
/* @GuardedBy(lock) */
private long nextID = System.currentTimeMillis();
/* @GuardedBy(lock) */
private final PermissionStorage permissionStorage;
private final Object lock = new Object();
private final Framework framework;
private final PermissionInfo[] impliedPermissionInfos;
private final EquinoxSecurityManager supportedSecurityManager;
public SecurityAdmin(EquinoxSecurityManager supportedSecurityManager, Framework framework, PermissionStorage permissionStorage) throws IOException {
this.supportedSecurityManager = supportedSecurityManager;
this.framework = framework;
this.permissionStorage = new SecurePermissionStorage(permissionStorage);
this.impliedPermissionInfos = SecurityAdmin.getPermissionInfos(getClass().getResource(Constants.OSGI_BASE_IMPLIED_PERMISSIONS), framework);
String[] encodedDefaultInfos = permissionStorage.getPermissionData(null);
PermissionInfo[] defaultInfos = getPermissionInfos(encodedDefaultInfos);
if (defaultInfos != null)
permAdminDefaults = new PermissionInfoCollection(defaultInfos);
String[] locations = permissionStorage.getLocations();
if (locations != null) {
for (int i = 0; i < locations.length; i++) {
String[] encodedLocationInfos = permissionStorage.getPermissionData(locations[i]);
if (encodedLocationInfos != null) {
PermissionInfo[] locationInfos = getPermissionInfos(encodedLocationInfos);
permAdminTable.setPermissions(locations[i], locationInfos);
}
}
}
String[] encodedCondPermInfos = permissionStorage.getConditionalPermissionInfos();
if (encodedCondPermInfos == null)
condAdminTable = new SecurityTable(this, new SecurityRow[0]);
else {
SecurityRow[] rows = new SecurityRow[encodedCondPermInfos.length];
for (int i = 0; i < rows.length; i++)
rows[i] = SecurityRow.createSecurityRow(this, encodedCondPermInfos[i]);
condAdminTable = new SecurityTable(this, rows);
}
}
private static PermissionInfo[] getPermissionInfos(String[] encodedInfos) {
if (encodedInfos == null)
return null;
PermissionInfo[] results = new PermissionInfo[encodedInfos.length];
for (int i = 0; i < results.length; i++)
results[i] = new PermissionInfo(encodedInfos[i]);
return results;
}
boolean checkPermission(Permission permission, BundlePermissions bundlePermissions) {
// check permissions by location
PermissionInfoCollection locationCollection;
SecurityTable curCondAdminTable;
PermissionInfoCollection curPermAdminDefaults;
// save off the current state of the world while holding the lock
synchronized (lock) {
// get location the hard way to avoid permission check
locationCollection = permAdminTable.getCollection(bundlePermissions.getBundle().getBundleData().getLocation());
curCondAdminTable = condAdminTable;
curPermAdminDefaults = permAdminDefaults;
}
if (locationCollection != null)
return locationCollection.implies(permission);
// if conditional admin table is empty the fall back to defaults
if (curCondAdminTable.isEmpty())
return curPermAdminDefaults != null ? curPermAdminDefaults.implies(permission) : DEFAULT_DEFAULT.implies(permission);
// check the condition table
int result = curCondAdminTable.evaluate(bundlePermissions, permission);
if ((result & SecurityTable.GRANTED) != 0)
return true;
if ((result & SecurityTable.DENIED) != 0)
return false;
if ((result & SecurityTable.POSTPONED) != 0)
return true;
return false;
}
public PermissionInfo[] getDefaultPermissions() {
synchronized (lock) {
if (permAdminDefaults == null)
return null;
return permAdminDefaults.getPermissionInfos();
}
}
public String[] getLocations() {
synchronized (lock) {
String[] results = permAdminTable.getLocations();
return results.length == 0 ? null : results;
}
}
public PermissionInfo[] getPermissions(String location) {
synchronized (lock) {
return permAdminTable.getPermissions(location);
}
}
public void setDefaultPermissions(PermissionInfo[] permissions) {
checkAllPermission();
synchronized (lock) {
if (permissions == null)
permAdminDefaults = null;
else
permAdminDefaults = new PermissionInfoCollection(permissions);
try {
permissionStorage.setPermissionData(null, getEncodedPermissionInfos(permissions));
} catch (IOException e) {
// log
e.printStackTrace();
}
}
}
private static void checkAllPermission() {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(new AllPermission());
}
private static String[] getEncodedPermissionInfos(PermissionInfo[] permissions) {
if (permissions == null)
return null;
String[] encoded = new String[permissions.length];
for (int i = 0; i < encoded.length; i++)
encoded[i] = permissions[i].getEncoded();
return encoded;
}
public void setPermissions(String location, PermissionInfo[] permissions) {
checkAllPermission();
synchronized (lock) {
permAdminTable.setPermissions(location, permissions);
try {
permissionStorage.setPermissionData(location, getEncodedPermissionInfos(permissions));
} catch (IOException e) {
// TODO log
e.printStackTrace();
}
}
}
void delete(SecurityRow securityRow, boolean firstTry) {
ConditionalPermissionUpdate update = newConditionalPermissionUpdate();
List rows = update.getConditionalPermissionInfos();
for (Iterator iRows = rows.iterator(); iRows.hasNext();) {
ConditionalPermissionInfo info = (ConditionalPermissionInfo) iRows.next();
if (securityRow.getName().equals(info.getName())) {
iRows.remove();
synchronized (lock) {
if (!update.commit()) {
if (firstTry)
// try again
delete(securityRow, false);
}
}
break;
}
}
}
/**
* @deprecated
*/
public ConditionalPermissionInfo addConditionalPermissionInfo(ConditionInfo[] conds, PermissionInfo[] perms) {
return setConditionalPermissionInfo(null, conds, perms, true);
}
public ConditionalPermissionInfo newConditionalPermissionInfo(String name, ConditionInfo[] conditions, PermissionInfo[] permissions, String decision) {
return new SecurityRowSnapShot(name, conditions, permissions, decision);
}
public ConditionalPermissionUpdate newConditionalPermissionUpdate() {
synchronized (lock) {
return new SecurityTableUpdate(this, condAdminTable.getRows(), timeStamp);
}
}
public AccessControlContext getAccessControlContext(String[] signers) {
Enumeration infos = getConditionalPermissionInfos();
ArrayList securityRows = new ArrayList();
if (infos != null && infos.hasMoreElements()) {
// enumerate through all the rows
while (infos.hasMoreElements()) {
SecurityRow condPermInfo = (SecurityRow) infos.nextElement();
ConditionInfo[] condInfo = condPermInfo.internalGetConditionInfos();
boolean match = true;
// check that each condition is a signer condition
for (int i = 0; match && i < condInfo.length; i++) {
if (BundleSignerCondition.class.getName().equals(condInfo[i].getType())) {
String[] args = condInfo[i].getArgs();
String condSigners = args.length > 0 ? args[0] : null;
if (condSigners != null) {
match = false;
boolean negate = (args.length == 2) ? "!".equals(args[1]) : false; //$NON-NLS-1$
for (int j = 0; j < signers.length && !match; j++)
match = (negate ^ DNChainMatching.match(signers[i], condSigners));
} else {
match = false;
}
} else {
// contains a non signer condition; cannot match
match = false;
}
}
if (match)
// found match add the row; keeping the row order
securityRows.add(condPermInfo);
}
} else {
PermissionCollection defaultPermissions;
synchronized (lock) {
defaultPermissions = permAdminDefaults == null ? DEFAULT_DEFAULT : permAdminDefaults;
}
return new AccessControlContext(new ProtectionDomain[] {new ProtectionDomain(null, defaultPermissions)});
}
SecurityTable table = new SecurityTable(this, (SecurityRow[]) securityRows.toArray(new SecurityRow[securityRows.size()]));
return new AccessControlContext(new ProtectionDomain[] {new ProtectionDomain(null, table)});
}
/**
* @deprecated
*/
public ConditionalPermissionInfo getConditionalPermissionInfo(String name) {
synchronized (lock) {
return condAdminTable.getRow(name);
}
}
/**
* @deprecated
*/
public Enumeration getConditionalPermissionInfos() {
// could implement our own Enumeration, but we don't care about performance here. Just do something simple:
synchronized (lock) {
SecurityRow[] rows = condAdminTable.getRows();
Vector vRows = new Vector(rows.length);
for (int i = 0; i < rows.length; i++)
vRows.add(rows[i]);
return vRows.elements();
}
}
/**
* @deprecated
*/
public ConditionalPermissionInfo setConditionalPermissionInfo(String name, ConditionInfo[] conds, PermissionInfo[] perms) {
return setConditionalPermissionInfo(name, conds, perms, true);
}
private ConditionalPermissionInfo setConditionalPermissionInfo(String name, ConditionInfo[] conds, PermissionInfo[] perms, boolean firstTry) {
ConditionalPermissionUpdate update = newConditionalPermissionUpdate();
List rows = update.getConditionalPermissionInfos();
ConditionalPermissionInfo infoBase = newConditionalPermissionInfo(name, conds, perms, ConditionalPermissionInfo.ALLOW);
int index = -1;
if (name == null) {
rows.add(infoBase);
index = rows.size() - 1;
} else {
for (int i = 0; i < rows.size() && index < 0; i++) {
ConditionalPermissionInfo info = (ConditionalPermissionInfo) rows.get(i);
if (name.equals(info.getName())) {
rows.set(i, infoBase);
index = i;
}
}
if (index < 0) {
rows.add(infoBase);
index = rows.size() - 1;
}
}
synchronized (lock) {
if (!update.commit()) {
if (firstTry)
// try again
setConditionalPermissionInfo(name, conds, perms, false);
}
return condAdminTable.getRow(index);
}
}
boolean commit(List rows, long updateStamp) {
checkAllPermission();
synchronized (lock) {
if (updateStamp != timeStamp)
return false;
SecurityRow[] newRows = new SecurityRow[rows.size()];
Collection names = new ArrayList();
for (int i = 0; i < newRows.length; i++) {
Object rowObj = rows.get(i);
if (!(rowObj instanceof ConditionalPermissionInfo))
throw new IllegalStateException("Invalid type \"" + rowObj.getClass().getName() + "\" at row: " + i); //$NON-NLS-1$//$NON-NLS-2$
ConditionalPermissionInfo infoBaseRow = (ConditionalPermissionInfo) rowObj;
String name = infoBaseRow.getName();
if (name == null)
name = generateName();
if (names.contains(name))
throw new IllegalStateException("Duplicate name \"" + name + "\" at row: " + i); //$NON-NLS-1$//$NON-NLS-2$
newRows[i] = new SecurityRow(this, name, infoBaseRow.getConditionInfos(), infoBaseRow.getPermissionInfos(), infoBaseRow.getGrantDecision());
}
condAdminTable = new SecurityTable(this, newRows);
try {
permissionStorage.saveConditionalPermissionInfos(condAdminTable.getEncodedRows());
} catch (IOException e) {
// TODO log
e.printStackTrace();
}
timeStamp += 1;
return true;
}
}
/* GuardedBy(lock) */
private String generateName() {
return "generated_" + Long.toString(nextID++); //$NON-NLS-1$;
}
public EquinoxProtectionDomain createProtectionDomain(AbstractBundle bundle) {
PermissionInfoCollection impliedPermissions = getImpliedPermission(bundle);
PermissionInfo[] restrictedInfos = getFileRelativeInfos(SecurityAdmin.getPermissionInfos(bundle.getEntry("OSGI-INF/permissions.perm"), framework), bundle); //$NON-NLS-1$
PermissionInfoCollection restrictedPermissions = restrictedInfos == null ? null : new PermissionInfoCollection(restrictedInfos);
BundlePermissions bundlePermissions = new BundlePermissions(bundle, this, impliedPermissions, restrictedPermissions);
return new EquinoxProtectionDomain(bundlePermissions);
}
private PermissionInfoCollection getImpliedPermission(AbstractBundle bundle) {
if (impliedPermissionInfos == null)
return null;
// create the implied AdminPermission actions for this bundle
PermissionInfo impliedAdminPermission = new PermissionInfo(AdminPermission.class.getName(), "(id=" + bundle.getBundleId() + ")", ADMIN_IMPLIED_ACTIONS); //$NON-NLS-1$ //$NON-NLS-2$
PermissionInfo[] bundleImpliedInfos = new PermissionInfo[impliedPermissionInfos.length + 1];
System.arraycopy(impliedPermissionInfos, 0, bundleImpliedInfos, 0, impliedPermissionInfos.length);
bundleImpliedInfos[impliedPermissionInfos.length] = impliedAdminPermission;
return new PermissionInfoCollection(getFileRelativeInfos(bundleImpliedInfos, bundle));
}
private PermissionInfo[] getFileRelativeInfos(PermissionInfo[] permissionInfos, AbstractBundle bundle) {
if (permissionInfos == null)
return null;
PermissionInfo[] results = new PermissionInfo[permissionInfos.length];
for (int i = 0; i < permissionInfos.length; i++) {
results[i] = permissionInfos[i];
if ("java.io.FilePermission".equals(permissionInfos[i].getType())) { //$NON-NLS-1$
File file = new File(permissionInfos[i].getName());
if (!file.isAbsolute()) { // relative name
File target = bundle.getBundleData().getDataFile(permissionInfos[i].getName());
if (target != null)
results[i] = new PermissionInfo(permissionInfos[i].getType(), target.getPath(), permissionInfos[i].getActions());
}
}
}
return results;
}
public void clearCaches() {
PermissionInfoCollection[] permAdminCollections;
SecurityRow[] condAdminRows;
synchronized (lock) {
permAdminCollections = permAdminTable.getCollections();
condAdminRows = condAdminTable.getRows();
}
for (int i = 0; i < permAdminCollections.length; i++)
permAdminCollections[i].clearPermissionCache();
for (int i = 0; i < condAdminRows.length; i++)
condAdminRows[i].clearCaches();
}
EquinoxSecurityManager getSupportedSecurityManager() {
return supportedSecurityManager != null ? supportedSecurityManager : getSupportedSystemSecurityManager();
}
static private EquinoxSecurityManager getSupportedSystemSecurityManager() {
try {
EquinoxSecurityManager equinoxManager = (EquinoxSecurityManager) System.getSecurityManager();
return equinoxManager != null && equinoxManager.inCheckPermission() ? equinoxManager : null;
} catch (ClassCastException e) {
return null;
}
}
private static PermissionInfo[] getPermissionInfos(URL resource, Framework framework) {
if (resource == null)
return null;
PermissionInfo[] info = EMPTY_PERM_INFO;
DataInputStream in = null;
try {
in = new DataInputStream(resource.openStream());
ArrayList permissions = new ArrayList();
BufferedReader reader;
try {
reader = new BufferedReader(new InputStreamReader(in, "UTF8")); //$NON-NLS-1$
} catch (UnsupportedEncodingException e) {
reader = new BufferedReader(new InputStreamReader(in));
}
while (true) {
String line = reader.readLine();
if (line == null) /* EOF */
break;
line = line.trim();
if ((line.length() == 0) || line.startsWith("#") || line.startsWith("//")) /* comments *///$NON-NLS-1$ //$NON-NLS-2$
continue;
try {
permissions.add(new PermissionInfo(line));
} catch (IllegalArgumentException iae) {
/* incorrectly encoded permission */
if (framework != null)
framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.getBundle(0), iae);
}
}
int size = permissions.size();
if (size > 0)
info = (PermissionInfo[]) permissions.toArray(new PermissionInfo[size]);
} catch (IOException e) {
// do nothing
} finally {
try {
if (in != null)
in.close();
} catch (IOException ee) {
// do nothing
}
}
return info;
}
}