| /******************************************************************************* |
| * 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; |
| } |
| } |