Add support for the 4.2.0 security specifications. This includes implementations of the access flag and various other changes (FELIX-1904, FELIX-22).
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@897721 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework.security/pom.xml b/framework.security/pom.xml
index c80eefb..81a7df2 100644
--- a/framework.security/pom.xml
+++ b/framework.security/pom.xml
@@ -34,7 +34,7 @@
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
- <version>4.1.0</version>
+ <version>4.2.0</version>
</dependency>
<dependency>
<groupId>${pom.groupId}</groupId>
diff --git a/framework.security/src/main/java/org/apache/felix/framework/SecurityActivator.java b/framework.security/src/main/java/org/apache/felix/framework/SecurityActivator.java
index f33b6fb..bc4a0bd 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/SecurityActivator.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/SecurityActivator.java
@@ -20,12 +20,9 @@
import java.io.File;
import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
import java.util.StringTokenizer;
-import java.util.Map.Entry;
+import org.apache.felix.framework.ext.SecurityProvider;
import org.apache.felix.framework.security.SecurityConstants;
import org.apache.felix.framework.security.condpermadmin.ConditionalPermissionAdminImpl;
import org.apache.felix.framework.security.permissionadmin.PermissionAdminImpl;
@@ -33,81 +30,72 @@
import org.apache.felix.framework.security.util.LocalPermissions;
import org.apache.felix.framework.security.util.Permissions;
import org.apache.felix.framework.security.util.PropertiesCache;
-import org.apache.felix.framework.security.verifier.BundleDNParser;
import org.apache.felix.framework.util.SecureAction;
-import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
import org.osgi.service.permissionadmin.PermissionAdmin;
/**
- * <p>This Felix specific activator installs a security provider with the Felix
+ * <p>
+ * This Felix specific activator installs a security provider with the Felix
* framework. The security settings can be changed via the
- * {@link PermissionAdmin} and/or the
- * {@link ConditionalPermissionAdmin} services that may be published by
- * this class.
+ * {@link PermissionAdmin} and/or the {@link ConditionalPermissionAdmin}
+ * services that may be published by this class.
* </p>
* <p>
- * Permission informations as well as caching data will be stored in several
- * files in a directory called <tt>security</tt> obtained by a call to
+ * Permission informations as well as caching data will be stored in several
+ * files in a directory called <tt>security</tt> obtained by a call to
* {@link BundleContext#getDataFile(String))}.
* </p>
* <p>
* The following properties are recognized:
* <p>
- * {@link SecurityConstants#ENABLE_PERMISSIONADMIN_PROP} - Whether or not
- * (<tt>true</tt>|<tt>false</tt>) to
- * publish a{@link ConditionalPermissionAdmin} service. The default is
+ * {@link SecurityConstants#ENABLE_PERMISSIONADMIN_PROP} - Whether or not (
+ * <tt>true</tt>|<tt>false</tt>) to publish a{@link ConditionalPermissionAdmin}
+ * service. The default is
* {@link SecurityConstants#ENABLE_PERMISSIONADMIN_VALUE}.
* </p>
* <p>
- * {@link SecurityConstants#ENABLE_CONDPERMADMIN_PROP} - Whether or not
- * (<tt>true</tt>|<tt>false</tt>) to
- * publish a{@link ConditionalPermissionAdmin} service. The default is
- * {@link SecurityConstants#ENABLE_CONDPERMADMIN_VALUE}.
+ * {@link SecurityConstants#ENABLE_CONDPERMADMIN_PROP} - Whether or not (
+ * <tt>true</tt>|<tt>false</tt>) to publish a{@link ConditionalPermissionAdmin}
+ * service. The default is {@link SecurityConstants#ENABLE_CONDPERMADMIN_VALUE}.
* </p>
* <p>
* {@link SecurityConstants#KEYSTORE_FILE_PROP} - The keystore URL(s) to use as
* trusted CA stores. The urls must be separated by a guard (i.e., <tt>|</tt>).
- * The default is
- * {@link SecurityConstants#KEYSTORE_FILE_VALUE}.
+ * The default is {@link SecurityConstants#KEYSTORE_FILE_VALUE}.
* </p>
* <p>
* {@link SecurityConstants#KEYSTORE_PASS_PROP} - The keystore password(s) to
- * use for the given keystores. The passwords must be separated by a guard
+ * use for the given keystores. The passwords must be separated by a guard
* (i.e., <tt>|</tt>).The default is
* {@link SecurityConstants#KEYSTORE_PASS_VALUE}.
* </p>
* <p>
* {@link SecurityConstants#KEYSTORE_TYPE_PROP} - The keystore type(s) to use
- * for the given keystores. The types must be separated by a guard
- * (i.e., <tt>|</tt>).The default is
- * {@link SecurityConstants#KEYSTORE_TYPE_VALUE}.
+ * for the given keystores. The types must be separated by a guard (i.e.,
+ * <tt>|</tt>).The default is {@link SecurityConstants#KEYSTORE_TYPE_VALUE}.
* </p>
* <p>
* {@link SecurityConstants#CRL_FILE_PROP} - The CRL URL(s) to use for revoked
- * certificates. The urls must be separated by a guard (i.e., <tt>|</tt>).
- * The default is {@link SecurityConstants#CRL_FILE_VALUE}.
+ * certificates. The urls must be separated by a guard (i.e., <tt>|</tt>). The
+ * default is {@link SecurityConstants#CRL_FILE_VALUE}.
* </p>
* </p>
*/
/*
* TODO: using a string for passwords is bad. We need to investigate
- * alternatives.
+ * alternatives.
*
- * TODO: we might want to allow for the recognized properties to
- * change without a restart. This is trick because we can not publish a managed
- * service due to not being able to import as we are an extension bundle.
+ * TODO: we might want to allow for the recognized properties to change without
+ * a restart. This is trick because we can not publish a managed service due to
+ * not being able to import as we are an extension bundle.
*/
public final class SecurityActivator implements BundleActivator
{
- private SecurityProviderImpl m_provider = null;
- private PropertiesCache m_dnsCache = null;
- private PropertiesCache m_localCache = null;
- private LocalPermissions m_localPermissions = null;
-
public synchronized void start(BundleContext context) throws Exception
{
PermissionAdminImpl pai = null;
@@ -135,15 +123,14 @@
SecurityConstants.ENABLE_PERMISSIONADMIN_PROP,
SecurityConstants.ENABLE_PERMISSIONADMIN_VALUE)))
{
- File cache =
- context.getDataFile("security" + File.separator + "pa.txt");
+ File cache = context.getDataFile("security" + File.separator
+ + "pa.txt");
if ((cache == null) || (!cache.isFile() && !cache.createNewFile()))
{
throw new IOException("Can't create cache file");
}
- pai =
- new PermissionAdminImpl(permissions, new PropertiesCache(cache,
- tmp, action));
+ pai = new PermissionAdminImpl(permissions, new PropertiesCache(
+ cache, tmp, action));
}
ConditionalPermissionAdminImpl cpai = null;
@@ -152,92 +139,72 @@
SecurityConstants.ENABLE_CONDPERMADMIN_PROP,
SecurityConstants.ENABLE_CONDPERMADMIN_VALUE)))
{
- File cpaCache =
- context.getDataFile("security" + File.separator + "cpa.txt");
- if ((cpaCache == null) || (!cpaCache.isFile() && !cpaCache.createNewFile()))
+ File cpaCache = context.getDataFile("security" + File.separator
+ + "cpa.txt");
+ if ((cpaCache == null)
+ || (!cpaCache.isFile() && !cpaCache.createNewFile()))
{
throw new IOException("Can't create cache file");
}
- File localCache =
- context.getDataFile("security" + File.separator + "local.txt");
- if ((localCache == null) || (!localCache.isFile() && !localCache.createNewFile()))
- {
- throw new IOException("Can't create cache file");
- }
-
- m_localCache = new PropertiesCache(localCache, tmp, action);
- m_localPermissions = new LocalPermissions(permissions, m_localCache);
-
- cpai =
- new ConditionalPermissionAdminImpl(permissions, new Conditions(
- action), m_localPermissions,
- new PropertiesCache(cpaCache, tmp, action));
+
+ LocalPermissions localPermissions = new LocalPermissions(
+ permissions);
+
+ cpai = new ConditionalPermissionAdminImpl(permissions,
+ new Conditions(action), localPermissions, new PropertiesCache(
+ cpaCache, tmp, action), pai);
}
if ((pai != null) || (cpai != null))
{
- String crlList =
- getProperty(context, SecurityConstants.CRL_FILE_PROP,
- SecurityConstants.CRL_FILE_VALUE);
- String storeList =
- getProperty(context, SecurityConstants.KEYSTORE_FILE_PROP,
- SecurityConstants.KEYSTORE_FILE_VALUE);
- String passwdList =
- getProperty(context, SecurityConstants.KEYSTORE_PASS_PROP,
- SecurityConstants.KEYSTORE_PASS_VALUE);
- String typeList =
- getProperty(context, SecurityConstants.KEYSTORE_TYPE_PROP,
- SecurityConstants.KEYSTORE_TYPE_VALUE);
+ String crlList = getProperty(context,
+ SecurityConstants.CRL_FILE_PROP,
+ SecurityConstants.CRL_FILE_VALUE);
+ String storeList = getProperty(context,
+ SecurityConstants.KEYSTORE_FILE_PROP,
+ SecurityConstants.KEYSTORE_FILE_VALUE);
+ String passwdList = getProperty(context,
+ SecurityConstants.KEYSTORE_PASS_PROP,
+ SecurityConstants.KEYSTORE_PASS_VALUE);
+ String typeList = getProperty(context,
+ SecurityConstants.KEYSTORE_TYPE_PROP,
+ SecurityConstants.KEYSTORE_TYPE_VALUE);
+ String osgi_keystores = getProperty(context,
+ Constants.FRAMEWORK_TRUST_REPOSITORIES, null);
+ if (osgi_keystores != null)
+ {
+ StringTokenizer tok = new StringTokenizer(osgi_keystores,
+ File.pathSeparator);
+
+ if (storeList.length() == 0)
+ {
+ storeList += "file:" + tok.nextToken();
+ passwdList += " ";
+ typeList += "JKS";
+ }
+ while (tok.hasMoreTokens())
+ {
+ storeList += "|file:" + tok.nextToken();
+ passwdList += "| ";
+ typeList += "|JKS";
+ }
+ }
StringTokenizer storeTok = new StringTokenizer(storeList, "|");
StringTokenizer passwdTok = new StringTokenizer(passwdList, "|");
StringTokenizer typeTok = new StringTokenizer(typeList, "|");
- if ((storeTok.countTokens() != passwdTok.countTokens())
- || (passwdTok.countTokens() != typeTok.countTokens()))
+ if ((storeTok.countTokens() != typeTok.countTokens())
+ || (passwdTok.countTokens() != storeTok.countTokens()))
{
throw new BundleException(
"Each CACerts keystore must have one type and one passwd entry and vice versa.");
}
- m_provider =
- new SecurityProviderImpl(crlList, typeList, passwdList,
- storeList, pai, cpai, action);
+ SecurityProvider provider = new SecurityProviderImpl(crlList,
+ typeList, passwdList, storeList, pai, cpai, action);
- File cache =
- context.getDataFile("security" + File.separator + "dns.txt");
- if ((cache == null) || (!cache.isFile() && !cache.createNewFile()))
- {
- throw new IOException("Can't create cache file");
- }
- m_dnsCache = new PropertiesCache(cache, tmp, action);
-
- Map store = m_dnsCache.read(String[].class);
-
- if (store != null)
- {
- BundleDNParser parser = m_provider.getParser();
-
- for (Iterator iter = store.entrySet().iterator(); iter
- .hasNext();)
- {
- Entry entry = (Entry) iter.next();
- String[] value = (String[]) entry.getValue();
- if ("none".equals(value[0]))
- {
- parser.put((String) entry.getKey(), null);
- }
- else if ("invalid".equals(value[0]))
- {
- parser.put((String) entry.getKey(), new String[0]);
- }
- else
- {
- parser.put((String) entry.getKey(), value);
- }
- }
- }
- ((Felix) context.getBundle(0)).setSecurityProvider(m_provider);
+ ((Felix) context.getBundle(0)).setSecurityProvider(provider);
}
if (pai != null)
@@ -254,59 +221,7 @@
public synchronized void stop(BundleContext context) throws Exception
{
- if (m_provider != null)
- {
- m_dnsCache.write(write(m_provider.getParser().getCache(), context));
- }
- if (m_localPermissions != null)
- {
- m_localCache.write(write(m_localPermissions.getStore(), context));
- }
- m_provider = null;
- m_dnsCache = null;
- m_localPermissions = null;
- }
-
- private Map write(Map cache, BundleContext context)
- {
- // Filter the cached dn chains and only store the latest for each
- // bundle. This is ok because the framework will prune old revisions
- // after a restart. The format is <id>-<timestamp>
- Map store = new HashMap();
- Map index = new HashMap();
- for (Iterator iter = cache.entrySet().iterator(); iter.hasNext();)
- {
- Entry entry = (Entry) iter.next();
-
- String key = (String) entry.getKey();
- String id = key.substring(0, key.indexOf("-"));
- String time = key.substring(key.indexOf("-") + 1);
- Bundle bundle = context.getBundle(Long.parseLong(id));
- long timeLong = Long.parseLong(time);
- if ((bundle == null) ||
- (bundle.getLastModified() > timeLong))
- {
- continue;
- }
- String last = (String) index.get(id);
-
- if ((last == null)
- || (Long.parseLong(last) < timeLong))
- {
- index.put(id, time);
- Object[] dns = (Object[]) entry.getValue();
- store.remove(id + "-" + last);
- if ((dns != null) && (dns.length > 0))
- {
- store.put(key, dns);
- }
- else
- {
- store.put(key, (dns == null) ? new String[] {"none"} : new String[] {"invalid"});
- }
- }
- }
- return store;
+ ((Felix) context.getBundle(0)).setSecurityProvider(null);
}
private String getProperty(BundleContext context, String key,
@@ -314,7 +229,6 @@
{
String result = context.getProperty(key);
- return ((result != null) && (result.trim().length() > 0)) ? result
- : defaultValue;
+ return (result != null) ? result : defaultValue;
}
}
diff --git a/framework.security/src/main/java/org/apache/felix/framework/SecurityProviderImpl.java b/framework.security/src/main/java/org/apache/felix/framework/SecurityProviderImpl.java
index f7f4eb0..1fadadd 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/SecurityProviderImpl.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/SecurityProviderImpl.java
@@ -26,13 +26,13 @@
import org.apache.felix.framework.security.permissionadmin.PermissionAdminImpl;
import org.apache.felix.framework.security.util.TrustManager;
import org.apache.felix.framework.security.verifier.BundleDNParser;
-import org.apache.felix.framework.security.verifier.SignerMatcher;
import org.apache.felix.framework.util.SecureAction;
+import org.apache.felix.moduleloader.IModule;
import org.osgi.framework.Bundle;
/**
- * This class is the entry point to the security. It is used to determine whether
- * a given bundle is signed correctely and has permissions based on
+ * This class is the entry point to the security. It is used to determine
+ * whether a given bundle is signed correctely and has permissions based on
* PermissionAdmin or ConditionalPermissionAdmin.
*/
public final class SecurityProviderImpl implements SecurityProvider
@@ -42,69 +42,49 @@
private final ConditionalPermissionAdminImpl m_cpai;
private final SecureAction m_action;
- SecurityProviderImpl(String crlList, String typeList,
- String passwdList, String storeList, PermissionAdminImpl pai,
+ SecurityProviderImpl(String crlList, String typeList, String passwdList,
+ String storeList, PermissionAdminImpl pai,
ConditionalPermissionAdminImpl cpai, SecureAction action)
{
m_pai = pai;
m_cpai = cpai;
m_action = action;
- m_parser =
- new BundleDNParser(new TrustManager(crlList, typeList, passwdList,
- storeList, m_action));
- }
-
- BundleDNParser getParser()
- {
- return m_parser;
+ m_parser = new BundleDNParser(new TrustManager(crlList, typeList,
+ passwdList, storeList, m_action));
}
/**
- * If the given bundle is signed but can not be verified (e.g., missing files)
- * then throw an exception.
+ * If the given bundle is signed but can not be verified (e.g., missing
+ * files) then throw an exception.
*/
public void checkBundle(Bundle bundle) throws Exception
{
- m_parser.checkDNChains(
- (Long.toString(bundle.getBundleId()) + "-" + bundle.getLastModified()),
- ((BundleImpl) bundle).getCurrentModule().getContent());
+ IModule module = ((BundleImpl) bundle).getCurrentModule();
+ m_parser.checkDNChains(module, module.getContent(),
+ Bundle.SIGNERS_TRUSTED);
}
/**
* Get a signer matcher that can be used to match digital signed bundles.
*/
- public Object getSignerMatcher(final Bundle bundle)
+ public Object getSignerMatcher(final Bundle bundle, int signersType)
{
- return new SignerMatcher(Long.toString(bundle.getBundleId()),
- bundle.getLastModified(),
- ((BundleImpl) bundle).getCurrentModule().getContent(),
- m_parser);
+ IModule module = ((BundleImpl) bundle).getCurrentModule();
+ return m_parser.getDNChains(module, module.getContent(), signersType);
}
- ThreadLocal loopCheck = new ThreadLocal();
-
/**
- * If we have a permissionadmin then ask that one first and have it
- * decide in case there is a location bound. If not then either use its
- * default permission in case there is no conditional permission admin
- * or else ask that one.
+ * If we have a permissionadmin then ask that one first and have it decide
+ * in case there is a location bound. If not then either use its default
+ * permission in case there is no conditional permission admin or else ask
+ * that one.
*/
public boolean hasBundlePermission(ProtectionDomain bundleProtectionDomain,
Permission permission, boolean direct)
{
- if (loopCheck.get() != null)
- {
- return true;
- }
- else
- {
- loopCheck.set(this);
- }
- try
- {
- BundleProtectionDomain pd =
- (BundleProtectionDomain) bundleProtectionDomain;
+ BundleProtectionDomain pd = (BundleProtectionDomain) bundleProtectionDomain;
BundleImpl bundle = pd.getBundle();
+ IModule module = pd.getModule();
if (bundle.getBundleId() == 0)
{
@@ -116,13 +96,22 @@
Boolean result = null;
if (m_pai != null)
{
- result =
- m_pai.hasPermission(bundle.getLocation(), pd.getBundle(),
- permission, m_cpai, pd);
+ result = m_pai.hasPermission(bundle._getLocation(), pd.getBundle(),
+ permission, m_cpai, pd, bundle.getCurrentModule().getContent());
}
if (result != null)
{
+ if ((m_cpai != null) && !direct)
+ {
+ boolean allow = result.booleanValue();
+ if (!allow)
+ {
+ m_cpai.clearPD();
+ return false;
+ }
+ return m_cpai.handlePAHandle(pd);
+ }
return result.booleanValue();
}
@@ -130,10 +119,7 @@
{
try
{
- return m_cpai.hasPermission(bundle,
- bundle.getCurrentModule().getContent(),
- bundle.getBundleId() + "-" +
- bundle.getLastModified(),null, pd,
+ return m_cpai.hasPermission(module, module.getContent(), pd,
permission, direct, m_pai);
}
catch (Exception e)
@@ -144,8 +130,5 @@
}
return false;
- } finally {
- loopCheck.set(null);
- }
}
}
\ No newline at end of file
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/SecurityConstants.java b/framework.security/src/main/java/org/apache/felix/framework/security/SecurityConstants.java
index 9ac88d9..790e185 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/SecurityConstants.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/SecurityConstants.java
@@ -18,38 +18,29 @@
*/
package org.apache.felix.framework.security;
-import java.io.File;
-
public interface SecurityConstants
{
public static final String KEYSTORE_FILE_PROP = "felix.keystore";
- public static final String KEYSTORE_FILE_VALUE = "file:" +
- System.getProperty("java.home") + File.separatorChar + "lib"
- + File.separatorChar + "security" + File.separatorChar + "cacerts"
- + "|file:" + System.getProperty("user.home") + File.separatorChar
- + ".keystore";
+ public static final String KEYSTORE_FILE_VALUE = "";
public static final String KEYSTORE_TYPE_PROP = "felix.keystore.type";
- public static final String KEYSTORE_TYPE_VALUE = "JKS" + "|" + "JKS";
+ public static final String KEYSTORE_TYPE_VALUE = "";
public static final String KEYSTORE_PASS_PROP = "felix.keystore.pass";
- public static final String KEYSTORE_PASS_VALUE =
- "changeit" + "|" + "changeit";
+ public static final String KEYSTORE_PASS_VALUE = "";
public static final String CRL_FILE_PROP = "felix.crl";
public static final String CRL_FILE_VALUE = "";
- public static final String ENABLE_CONDPERMADMIN_PROP =
- "felix.security.conpermadmin";
+ public static final String ENABLE_CONDPERMADMIN_PROP = "felix.security.conpermadmin";
public static final String ENABLE_CONDPERMADMIN_VALUE = "true";
- public static final String ENABLE_PERMISSIONADMIN_PROP =
- "felix.security.permissionadmin";
+ public static final String ENABLE_PERMISSIONADMIN_PROP = "felix.security.permissionadmin";
public static final String ENABLE_PERMISSIONADMIN_VALUE = "true";
}
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/ConditionalPermissionAdminImpl.java b/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/ConditionalPermissionAdminImpl.java
index ad73e75..04e6de7 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/ConditionalPermissionAdminImpl.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/ConditionalPermissionAdminImpl.java
@@ -19,70 +19,222 @@
package org.apache.felix.framework.security.condpermadmin;
import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.net.URL;
import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.DomainCombiner;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
import java.security.Permission;
+import java.security.Principal;
import java.security.ProtectionDomain;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.X509Certificate;
+import java.util.AbstractSet;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
+import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
import java.util.Map.Entry;
+import org.apache.felix.framework.BundleProtectionDomain;
+import org.apache.felix.framework.security.permissionadmin.PermissionAdminImpl;
import org.apache.felix.framework.security.util.Conditions;
import org.apache.felix.framework.security.util.LocalPermissions;
import org.apache.felix.framework.security.util.Permissions;
import org.apache.felix.framework.security.util.PropertiesCache;
import org.apache.felix.framework.util.IteratorToEnumeration;
+import org.apache.felix.framework.util.manifestparser.R4Library;
+import org.apache.felix.moduleloader.ICapability;
import org.apache.felix.moduleloader.IContent;
+import org.apache.felix.moduleloader.IModule;
+import org.apache.felix.moduleloader.IRequirement;
+import org.apache.felix.moduleloader.IWire;
import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
import org.osgi.service.condpermadmin.ConditionInfo;
import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
import org.osgi.service.condpermadmin.ConditionalPermissionInfo;
+import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
import org.osgi.service.permissionadmin.PermissionInfo;
/**
* An implementation of the ConditionalPermissionAdmin service that doesn't need
* to have a framework specific security manager set. It use the DomainGripper
- * to know what bundleprotectiondomains are expected.
+ * to know what bundleprotectiondomains are expected.
*/
public final class ConditionalPermissionAdminImpl implements
ConditionalPermissionAdmin
{
+ private static class OrderedHashMap extends HashMap
+ {
+ private final List m_order = new ArrayList();
+
+ public Object put(Object key, Object value)
+ {
+ Object result = super.put(key, value);
+ if (result != value)
+ {
+ m_order.remove(key);
+ m_order.add(key);
+ }
+ return result;
+ };
+
+ public void putAll(Map map)
+ {
+ for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
+ {
+ Entry entry = (Entry) iter.next();
+ put(entry.getKey(), entry.getValue());
+ }
+ };
+
+ public Set keySet()
+ {
+ return new AbstractSet()
+ {
+ public Iterator iterator()
+ {
+ return m_order.iterator();
+ }
+
+ public int size()
+ {
+ return m_order.size();
+ }
+
+ };
+ };
+
+ public Set entrySet()
+ {
+ return new AbstractSet()
+ {
+
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ Iterator m_iter = m_order.iterator();
+
+ public boolean hasNext()
+ {
+ return m_iter.hasNext();
+ }
+
+ public Object next()
+ {
+ final Object key = m_iter.next();
+ return new Entry()
+ {
+
+ public Object getKey()
+ {
+ return key;
+ }
+
+ public Object getValue()
+ {
+ return get(key);
+ }
+
+ public Object setValue(Object arg0)
+ {
+ throw new IllegalStateException(
+ "Not Implemented");
+ }
+ };
+ }
+
+ public void remove()
+ {
+ throw new IllegalStateException("Not Implemented");
+ }
+
+ };
+ }
+
+ public int size()
+ {
+ return m_order.size();
+ }
+
+ };
+ };
+
+ public Collection values()
+ {
+ List result = new ArrayList();
+ for (Iterator iter = m_order.iterator(); iter.hasNext();)
+ {
+ result.add(super.get(iter.next()));
+ }
+ return result;
+ };
+
+ public Object remove(Object key)
+ {
+ Object result = super.remove(key);
+ if (result != null)
+ {
+ m_order.remove(key);
+ }
+ return result;
+ };
+
+ public void clear()
+ {
+ super.clear();
+ m_order.clear();
+ };
+ };
+
private static final ConditionInfo[] EMPTY_CONDITION_INFO = new ConditionInfo[0];
private static final PermissionInfo[] EMPTY_PERMISSION_INFO = new PermissionInfo[0];
- private final Map m_condPermInfos = new HashMap();
+ private final Map m_condPermInfos = new OrderedHashMap();
private final PropertiesCache m_propertiesCache;
private final Permissions m_permissions;
private final Conditions m_conditions;
private final LocalPermissions m_localPermissions;
+ private final PermissionAdminImpl m_pai;
public ConditionalPermissionAdminImpl(Permissions permissions,
Conditions condtions, LocalPermissions localPermissions,
- PropertiesCache cache) throws IOException
+ PropertiesCache cache, PermissionAdminImpl pai) throws IOException
{
m_propertiesCache = cache;
m_permissions = permissions;
m_conditions = condtions;
m_localPermissions = localPermissions;
+ Map old = new OrderedHashMap();
// Now try to restore the cache.
- Map old = m_propertiesCache.read(ConditionalPermissionInfoImpl.class);
- if (old != null)
+ m_propertiesCache.read(ConditionalPermissionInfoImpl.class, old);
+ for (Iterator iter = old.entrySet().iterator(); iter.hasNext();)
{
- for (Iterator iter = old.entrySet().iterator(); iter.hasNext();)
- {
- Entry entry = (Entry) iter.next();
- String name = (String) entry.getKey();
- ConditionalPermissionInfoImpl cpi =
- ((ConditionalPermissionInfoImpl) entry.getValue());
- m_condPermInfos.put(name, new ConditionalPermissionInfoImpl(
- name, cpi._getConditionInfos(), cpi._getPermissionInfos(),
- this));
- }
+ Entry entry = (Entry) iter.next();
+ String name = (String) entry.getKey();
+ ConditionalPermissionInfoImpl cpi = ((ConditionalPermissionInfoImpl) entry
+ .getValue());
+ m_condPermInfos.put(name, new ConditionalPermissionInfoImpl(name,
+ cpi._getConditionInfos(), cpi._getPermissionInfos(), this, cpi
+ .isAllow()));
}
+ m_pai = pai;
}
public ConditionalPermissionInfo addConditionalPermissionInfo(
@@ -93,9 +245,8 @@
{
((SecurityManager) sm).checkPermission(Permissions.ALL_PERMISSION);
}
- ConditionalPermissionInfoImpl result =
- new ConditionalPermissionInfoImpl(notNull(conditions),
- notNull(permissions), this);
+ ConditionalPermissionInfoImpl result = new ConditionalPermissionInfoImpl(
+ notNull(conditions), notNull(permissions), this, true);
return write(result.getName(), result);
}
@@ -109,7 +260,8 @@
synchronized (m_condPermInfos)
{
- tmp = new HashMap(m_condPermInfos);
+ tmp = new OrderedHashMap();
+ tmp.putAll(m_condPermInfos);
if ((name != null) && (cpi != null))
{
@@ -133,8 +285,11 @@
{
synchronized (m_condPermInfos)
{
- m_condPermInfos.clear();
- m_condPermInfos.putAll(tmp);
+ if (tmp != null)
+ {
+ m_condPermInfos.clear();
+ m_condPermInfos.putAll(tmp);
+ }
}
ex.printStackTrace();
throw new IllegalStateException(ex.getMessage());
@@ -146,28 +301,485 @@
}
}
- // TODO: this is pretty much untested so it might not work like this
- public AccessControlContext getAccessControlContext(String[] signers)
+ private static class FakeBundle implements Bundle
{
- final String[] finalSigners =
- (String[]) notNull(signers).toArray(new String[0]);
- return new AccessControlContext(AccessController.getContext(),
- new DomainCombiner()
+ private final Map m_certs;
+
+ public FakeBundle(Map certs)
+ {
+ m_certs = Collections.unmodifiableMap(certs);
+ }
+
+ public Enumeration findEntries(String arg0, String arg1, boolean arg2)
+ {
+ return null;
+ }
+
+ public BundleContext getBundleContext()
+ {
+ return null;
+ }
+
+ public long getBundleId()
+ {
+ return -1;
+ }
+
+ public URL getEntry(String arg0)
+ {
+ return null;
+ }
+
+ public Enumeration getEntryPaths(String arg0)
+ {
+ return null;
+ }
+
+ public Dictionary getHeaders()
+ {
+ return new Hashtable();
+ }
+
+ public Dictionary getHeaders(String arg0)
+ {
+ return new Hashtable();
+ }
+
+ public long getLastModified()
+ {
+ return 0;
+ }
+
+ public String getLocation()
+ {
+ return "";
+ }
+
+ public ServiceReference[] getRegisteredServices()
+ {
+ return null;
+ }
+
+ public URL getResource(String arg0)
+ {
+ return null;
+ }
+
+ public Enumeration getResources(String arg0) throws IOException
+ {
+ return null;
+ }
+
+ public ServiceReference[] getServicesInUse()
+ {
+ return null;
+ }
+
+ public Map getSignerCertificates(int arg0)
+ {
+ return m_certs;
+ }
+
+ public int getState()
+ {
+ return Bundle.UNINSTALLED;
+ }
+
+ public String getSymbolicName()
+ {
+ return null;
+ }
+
+ public Version getVersion()
+ {
+ return Version.emptyVersion;
+ }
+
+ public boolean hasPermission(Object arg0)
+ {
+ return false;
+ }
+
+ public Class loadClass(String arg0) throws ClassNotFoundException
+ {
+ return null;
+ }
+
+ public void start() throws BundleException
+ {
+ throw new IllegalStateException();
+ }
+
+ public void start(int arg0) throws BundleException
+ {
+ throw new IllegalStateException();
+ }
+
+ public void stop() throws BundleException
+ {
+ throw new IllegalStateException();
+ }
+
+ public void stop(int arg0) throws BundleException
+ {
+ throw new IllegalStateException();
+ }
+
+ public void uninstall() throws BundleException
+ {
+ throw new IllegalStateException();
+ }
+
+ public void update() throws BundleException
+ {
+ throw new IllegalStateException();
+ }
+
+ public void update(InputStream arg0) throws BundleException
+ {
+ throw new IllegalStateException();
+ }
+
+ public boolean equals(Object o)
+ {
+ return this == o;
+ }
+
+ public int hashCode()
+ {
+ return System.identityHashCode(this);
+ }
+ }
+
+ private static class FakeCert extends X509Certificate
+ {
+ private final Principal m_principal;
+
+ public FakeCert(final String principal)
+ {
+ m_principal = new Principal()
{
- public ProtectionDomain[] combine(ProtectionDomain[] arg0,
- ProtectionDomain[] arg1)
+ public String getName()
{
- return new ProtectionDomain[] { new ProtectionDomain(null,
- null)
- {
- public boolean implies(Permission permission)
- {
- return hasPermission(null, null, null, finalSigners,
- this, permission, true, null);
- }
- } };
+ return principal;
}
- });
+ };
+ }
+
+ public void checkValidity()
+ throws java.security.cert.CertificateExpiredException,
+ java.security.cert.CertificateNotYetValidException
+ {
+
+ }
+
+ public void checkValidity(Date date)
+ throws java.security.cert.CertificateExpiredException,
+ java.security.cert.CertificateNotYetValidException
+ {
+ }
+
+ public int getBasicConstraints()
+ {
+ return 0;
+ }
+
+ public Principal getIssuerDN()
+ {
+ return null;
+ }
+
+ public boolean[] getIssuerUniqueID()
+ {
+ return null;
+ }
+
+ public boolean[] getKeyUsage()
+ {
+ return null;
+ }
+
+ public Date getNotAfter()
+ {
+ return null;
+ }
+
+ public Date getNotBefore()
+ {
+ return null;
+ }
+
+ public BigInteger getSerialNumber()
+ {
+ return null;
+ }
+
+ public String getSigAlgName()
+ {
+ return null;
+ }
+
+ public String getSigAlgOID()
+ {
+ return null;
+ }
+
+ public byte[] getSigAlgParams()
+ {
+ return null;
+ }
+
+ public byte[] getSignature()
+ {
+ return null;
+ }
+
+ public Principal getSubjectDN()
+ {
+ return m_principal;
+ }
+
+ public boolean[] getSubjectUniqueID()
+ {
+ return null;
+ }
+
+ public byte[] getTBSCertificate()
+ throws java.security.cert.CertificateEncodingException
+ {
+ return null;
+ }
+
+ public int getVersion()
+ {
+ return 0;
+ }
+
+ public byte[] getEncoded()
+ throws java.security.cert.CertificateEncodingException
+ {
+ return null;
+ }
+
+ public PublicKey getPublicKey()
+ {
+ return null;
+ }
+
+ public String toString()
+ {
+ return m_principal.getName();
+ }
+
+ public void verify(PublicKey key)
+ throws java.security.cert.CertificateException,
+ NoSuchAlgorithmException, InvalidKeyException,
+ NoSuchProviderException, SignatureException
+ {
+
+ }
+
+ public void verify(PublicKey key, String sigProvider)
+ throws java.security.cert.CertificateException,
+ NoSuchAlgorithmException, InvalidKeyException,
+ NoSuchProviderException, SignatureException
+ {
+
+ }
+
+ public Set getCriticalExtensionOIDs()
+ {
+ return null;
+ }
+
+ public byte[] getExtensionValue(String arg0)
+ {
+ return null;
+ }
+
+ public Set getNonCriticalExtensionOIDs()
+ {
+ return null;
+ }
+
+ public boolean hasUnsupportedCriticalExtension()
+ {
+ return false;
+ }
+
+ public boolean equals(Object o)
+ {
+ return this == o;
+ }
+
+ public int hashCode()
+ {
+ return System.identityHashCode(this);
+ }
+
+ }
+
+ public AccessControlContext getAccessControlContext(final String[] signers)
+ {
+ Map certificates = new HashMap();
+ for (int i = 0; i < signers.length; i++)
+ {
+ StringTokenizer tok = new StringTokenizer(signers[i], ";");
+ List certsList = new ArrayList();
+ while (tok.hasMoreTokens())
+ {
+ certsList.add(tok.nextToken());
+ }
+ String[] certs = (String[]) certsList.toArray(new String[certsList
+ .size()]);
+
+ X509Certificate key = new FakeCert(certs[0]);
+ List certList = new ArrayList();
+ certificates.put(key, certList);
+ certList.add(key);
+ for (int j = 1; j < certs.length; j++)
+ {
+ certList.add(new FakeCert(certs[j]));
+ }
+ }
+ final Bundle fake = new FakeBundle(certificates);
+ ProtectionDomain domain = new ProtectionDomain(null, null)
+ {
+ public boolean implies(Permission permission)
+ {
+ List posts = new ArrayList();
+ Boolean result = m_pai.hasPermission("", fake, permission,
+ ConditionalPermissionAdminImpl.this, this, null);
+ if (result != null)
+ {
+ return result.booleanValue();
+ }
+ if (eval(posts, new IModule()
+ {
+
+ public Bundle getBundle()
+ {
+ return fake;
+ }
+
+ public ICapability[] getCapabilities()
+ {
+ return null;
+ }
+
+ public Class getClassByDelegation(String arg0)
+ throws ClassNotFoundException
+ {
+ return null;
+ }
+
+ public IContent getContent()
+ {
+ return null;
+ }
+
+ public int getDeclaredActivationPolicy()
+ {
+ return 0;
+ }
+
+ public IRequirement[] getDynamicRequirements()
+ {
+ return null;
+ }
+
+ public URL getEntry(String arg0)
+ {
+ return null;
+ }
+
+ public Map getHeaders()
+ {
+ return null;
+ }
+
+ public String getId()
+ {
+ return null;
+ }
+
+ public InputStream getInputStream(int arg0, String arg1)
+ throws IOException
+ {
+ return null;
+ }
+
+ public R4Library[] getNativeLibraries()
+ {
+ return null;
+ }
+
+ public IRequirement[] getRequirements()
+ {
+ return null;
+ }
+
+ public URL getResourceByDelegation(String arg0)
+ {
+ return null;
+ }
+
+ public Enumeration getResourcesByDelegation(String arg0)
+ {
+ return null;
+ }
+
+ public Object getSecurityContext()
+ {
+ return null;
+ }
+
+ public String getSymbolicName()
+ {
+ return null;
+ }
+
+ public Version getVersion()
+ {
+ return null;
+ }
+
+ public IWire[] getWires()
+ {
+ return null;
+ }
+
+ public boolean hasInputStream(int arg0, String arg1)
+ throws IOException
+ {
+ return false;
+ }
+
+ public boolean isExtension()
+ {
+ return false;
+ }
+
+ public boolean isResolved()
+ {
+ return false;
+ }
+
+ public void setSecurityContext(Object arg0)
+ {
+ }
+ }, permission, m_pai))
+ {
+ if (!posts.isEmpty())
+ {
+ return m_conditions.evalRecursive(posts);
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+ return new AccessControlContext(new ProtectionDomain[] { domain });
}
public ConditionalPermissionInfo getConditionalPermissionInfo(String name)
@@ -185,7 +797,7 @@
if (result == null)
{
- result = new ConditionalPermissionInfoImpl(this, name);
+ result = new ConditionalPermissionInfoImpl(this, name, true);
result = write(result.getName(), result);
}
@@ -219,14 +831,13 @@
{
synchronized (m_condPermInfos)
{
- result =
- (ConditionalPermissionInfoImpl) m_condPermInfos.get(name);
+ result = (ConditionalPermissionInfoImpl) m_condPermInfos
+ .get(name);
if (result == null)
{
- result =
- new ConditionalPermissionInfoImpl(name, conditions,
- permissions, this);
+ result = new ConditionalPermissionInfoImpl(name,
+ conditions, permissions, this, true);
}
else
{
@@ -236,8 +847,8 @@
}
else
{
- result =
- new ConditionalPermissionInfoImpl(conditions, permissions, this);
+ result = new ConditionalPermissionInfoImpl(conditions, permissions,
+ this, true);
}
return write(result.getName(), result);
@@ -284,87 +895,84 @@
/**
* This method does the actual permission check. If it is not a direct check
- * it will try to determine the other bundle domains that will follow
- * automatically in case this is the first check in one permission check.
- * If not then it will keep track of which domains we have already see.
- * While it keeps track it builds up a list of postponed tuples which
- * it will evaluate at the last domain. See the core spec 9.5.1 and following
- * for a general description.
+ * it will try to determine the other bundle domains that will follow
+ * automatically in case this is the first check in one permission check. If
+ * not then it will keep track of which domains we have already see. While
+ * it keeps track it builds up a list of postponed tuples which it will
+ * evaluate at the last domain. See the core spec 9.5.1 and following for a
+ * general description.
*
- * @param felixBundle the bundle in question.
- * @param loader the content loader of the bundle to get access to the jar
- * to check for local permissions.
- * @param root the bundle id.
- * @param signers the signers (this is to support the ACC based on signers)
- * @param pd the bundle protection domain
- * @param permission the permission currently checked
- * @param direct whether this is a direct check or not. direct check will not
- * expect any further bundle domains on the stack
- * @return true in case the permission is granted or there are postponed tuples
- * false if not. Again, see the spec for more explanations.
+ * @param felixBundle
+ * the bundle in question.
+ * @param loader
+ * the content loader of the bundle to get access to the jar to
+ * check for local permissions.
+ * @param root
+ * the bundle id.
+ * @param signers
+ * the signers (this is to support the ACC based on signers)
+ * @param pd
+ * the bundle protection domain
+ * @param permission
+ * the permission currently checked
+ * @param direct
+ * whether this is a direct check or not. direct check will not
+ * expect any further bundle domains on the stack
+ * @return true in case the permission is granted or there are postponed
+ * tuples false if not. Again, see the spec for more explanations.
*/
- public boolean hasPermission(Bundle felixBundle, IContent content, String root,
- String[] signers, ProtectionDomain pd, Permission permission,
- boolean direct, Object admin)
+ public boolean hasPermission(IModule module, IContent content,
+ ProtectionDomain pd, Permission permission, boolean direct, Object admin)
{
// System.out.println(felixBundle + "-" + permission);
List domains = null;
List tuples = null;
Object[] entry = null;
- // first see whether this is the normal case (the special case is for
+ // first see whether this is the normal case (the special case is for
// the ACC based on signers).
- if (signers == null)
+ // In case of a direct call we don't need to look for other pds
+ if (direct)
{
- // In case of a direct call we don't need to look for other pds
- if (direct)
+ domains = new ArrayList();
+ tuples = new ArrayList();
+ domains.add(pd);
+ }
+ else
+ {
+ // Get the other pds from the stck
+ entry = (Object[]) m_stack.get();
+
+ // if there are none then get them from the gripper
+ if (entry == null)
{
- domains = new ArrayList();
- tuples = new ArrayList();
- domains.add(pd);
+ entry = new Object[] { new ArrayList(DomainGripper.grab()),
+ new ArrayList() };
}
else
{
- // Get the other pds from the stck
- entry = (Object[]) m_stack.get();
+ m_stack.set(null);
+ }
- // if there are none then get them from the gripper
- if (entry == null)
- {
- entry =
- new Object[] { new ArrayList(DomainGripper.grab()),
- new ArrayList() };
- }
- else
- {
- m_stack.set(null);
- }
-
- domains = (List) entry[0];
- tuples = (List) entry[1];
- if (!domains.contains(pd))
- {
- // We have been called directly without the direct flag
- domains.clear();
- domains.add(pd);
- }
+ domains = (List) entry[0];
+ tuples = (List) entry[1];
+ if (!domains.contains(pd))
+ {
+ // We have been called directly without the direct flag
+ domains.clear();
+ domains.add(pd);
}
}
// check the local permissions. they need to all the permission if there
// are any
- if (!m_localPermissions.implies(root, content, felixBundle, permission))
+ if (!impliesLocal(module.getBundle(), content, permission))
{
return false;
}
List posts = new ArrayList();
- boolean result = eval(posts, felixBundle, signers, permission, admin);
-
- if (signers != null)
- {
- return result;
- }
+ boolean result = eval(posts, module, permission, admin);
domains.remove(pd);
@@ -378,7 +986,8 @@
if (domains.isEmpty())
{
m_stack.set(null);
- // Now eval the postponed tupels. if the previous eval did return false
+ // Now eval the postponed tupels. if the previous eval did return
+ // false
// tuples will be empty so we don't return from here.
if (!tuples.isEmpty())
{
@@ -387,7 +996,8 @@
}
else
{
- // this is to support recursive permission checks. In case we trigger
+ // this is to support recursive permission checks. In case we
+ // trigger
// a permission check while eval the stack is null until this point
m_stack.set(entry);
}
@@ -395,6 +1005,12 @@
return result;
}
+ public boolean impliesLocal(Bundle felixBundle, IContent content,
+ Permission permission)
+ {
+ return m_localPermissions.implies(content, felixBundle, permission);
+ }
+
public boolean isEmpty()
{
synchronized (m_condPermInfos)
@@ -408,8 +1024,8 @@
// then we make sure their permissions imply the permission and add them
// to the list of posts. Return true in case we pass or have posts
// else falls and clear the posts first.
- private boolean eval(List posts, Bundle bundle, String[] signers,
- Permission permission, Object admin)
+ private boolean eval(List posts, IModule module, Permission permission,
+ Object admin)
{
List condPermInfos = null;
@@ -423,25 +1039,26 @@
}
// Check for implicit permissions like access to file area
- if ((bundle != null)
- && m_permissions.getPermissions(m_permissions.getImplicit(bundle))
- .implies(permission, bundle))
+ if (m_permissions.getPermissions(
+ m_permissions.getImplicit(module.getBundle())).implies(permission,
+ module.getBundle()))
{
return true;
}
-
+ List pls = new ArrayList();
// now do the real thing
for (Iterator iter = condPermInfos.iterator(); iter.hasNext();)
{
- ConditionalPermissionInfoImpl cpi =
- (ConditionalPermissionInfoImpl) iter.next();
+ ConditionalPermissionInfoImpl cpi = (ConditionalPermissionInfoImpl) iter
+ .next();
ConditionInfo[] conditions = cpi._getConditionInfos();
List currentPosts = new ArrayList();
- if (!m_conditions.getConditions(bundle, signers, conditions)
- .isSatisfied(currentPosts))
+ Conditions conds = m_conditions.getConditions(module, conditions);
+ if (!conds.isSatisfied(currentPosts, m_permissions
+ .getPermissions(cpi._getPermissionInfos()), permission))
{
continue;
}
@@ -454,13 +1071,127 @@
if (currentPosts.isEmpty())
{
- posts.clear();
+ pls.add(new Object[] { cpi, null });
+ break;
+ }
+ pls.add(new Object[] { cpi, currentPosts, conds });
+ }
+ while (pls.size() > 1)
+ {
+ if (!((ConditionalPermissionInfoImpl) ((Object[]) pls.get(pls
+ .size() - 1))[0]).isAllow())
+ {
+ pls.remove(pls.size() - 1);
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (pls.size() == 1)
+ {
+ if (((Object[]) pls.get(0))[1] != null)
+ {
+ posts.add(pls.get(0));
+ }
+ return ((ConditionalPermissionInfoImpl) ((Object[]) pls.get(0))[0])
+ .isAllow();
+ }
+ for (Iterator iter = pls.iterator(); iter.hasNext();)
+ {
+ posts.add(iter.next());
+ }
+ return !posts.isEmpty();
+ }
+
+ public ConditionalPermissionInfo newConditionalPermissionInfo(
+ String encodedConditionalPermissionInfo)
+ {
+ return new ConditionalPermissionInfoImpl(
+ encodedConditionalPermissionInfo);
+ }
+
+ public ConditionalPermissionInfo newConditionalPermissionInfo(String name,
+ ConditionInfo[] conditions, PermissionInfo[] permissions, String access)
+ {
+ return new ConditionalPermissionInfoImpl(name, conditions, permissions,
+ ConditionalPermissionAdminImpl.this, access
+ .equals(ConditionalPermissionInfo.ALLOW));
+ }
+
+ public ConditionalPermissionUpdate newConditionalPermissionUpdate()
+ {
+ return new ConditionalPermissionUpdate()
+ {
+ List current = null;
+ List out = null;
+ {
+ synchronized (m_condPermInfos)
+ {
+ current = new ArrayList(m_condPermInfos.values());
+ out = new ArrayList(m_condPermInfos.values());
+ }
+ }
+
+ public boolean commit()
+ {
+ synchronized (m_condPermInfos)
+ {
+ if (current.equals(new ArrayList(m_condPermInfos.values())))
+ {
+ m_condPermInfos.clear();
+ write(null, null);
+ for (Iterator iter = out.iterator(); iter.hasNext();)
+ {
+ ConditionalPermissionInfoImpl cpii = (ConditionalPermissionInfoImpl) iter
+ .next();
+ write(cpii.getName(), cpii);
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
return true;
}
- posts.add(currentPosts);
+ public List getConditionalPermissionInfos()
+ {
+ return out;
+ }
+ };
+ }
+
+ public boolean handlePAHandle(BundleProtectionDomain pd)
+ {
+ Object[] entry = (Object[]) m_stack.get();
+
+ if (entry == null)
+ {
+ entry = new Object[] { new ArrayList(DomainGripper.grab()),
+ new ArrayList() };
}
- return !posts.isEmpty();
+ ((List) entry[0]).remove(pd);
+ if (((List) entry[0]).isEmpty())
+ {
+ m_stack.set(null);
+ if (!((List) entry[1]).isEmpty())
+ {
+ return m_conditions.evalRecursive(((List) entry[1]));
+ }
+ }
+ else
+ {
+ m_stack.set(entry);
+ }
+
+ return true;
+ }
+
+ public void clearPD()
+ {
+ m_stack.set(null);
}
}
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/ConditionalPermissionInfoImpl.java b/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/ConditionalPermissionInfoImpl.java
index cfc4cb8..4e4bc13 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/ConditionalPermissionInfoImpl.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/ConditionalPermissionInfoImpl.java
@@ -30,15 +30,17 @@
/**
* Simple storage class for condperminfos. Additionally, this class can be used
- * to encode and decode infos.
+ * to encode and decode infos.
*/
-public final class ConditionalPermissionInfoImpl implements ConditionalPermissionInfo
+public final class ConditionalPermissionInfoImpl implements
+ ConditionalPermissionInfo
{
private static final Random RANDOM = new Random();
static final ConditionInfo[] CONDITION_INFO = new ConditionInfo[0];
static final PermissionInfo[] PERMISSION_INFO = new PermissionInfo[0];
private final Object m_lock = new Object();
private final String m_name;
+ private final boolean m_allow;
private volatile ConditionalPermissionAdminImpl m_cpai;
private ConditionInfo[] m_conditions;
private PermissionInfo[] m_permissions;
@@ -46,16 +48,18 @@
public ConditionalPermissionInfoImpl(String encoded)
{
StringTokenizer tok = new StringTokenizer(encoded, "\n");
- if (!tok.nextToken().trim().equals("{"))
+ String access = tok.nextToken().trim();
+ if (!(access.equals("ALLOW {") || access.equals("DENY {")))
{
throw new IllegalArgumentException();
}
+ m_allow = access.equals("ALLOW {");
m_cpai = null;
m_name = tok.nextToken().trim().substring(1);
List conditions = new ArrayList();
List permissions = new ArrayList();
- for (String current = tok.nextToken().trim();; current =
- tok.nextToken().trim())
+ for (String current = tok.nextToken().trim();; current = tok
+ .nextToken().trim())
{
if (current.equals("}"))
{
@@ -78,19 +82,18 @@
}
}
- m_conditions =
- conditions.isEmpty() ? CONDITION_INFO
- : (ConditionInfo[]) conditions
- .toArray(new ConditionInfo[conditions.size()]);
- m_permissions =
- permissions.isEmpty() ? PERMISSION_INFO
- : (PermissionInfo[]) permissions
- .toArray(new PermissionInfo[permissions.size()]);
+ m_conditions = conditions.isEmpty() ? CONDITION_INFO
+ : (ConditionInfo[]) conditions.toArray(new ConditionInfo[conditions
+ .size()]);
+ m_permissions = permissions.isEmpty() ? PERMISSION_INFO
+ : (PermissionInfo[]) permissions
+ .toArray(new PermissionInfo[permissions.size()]);
}
public ConditionalPermissionInfoImpl(ConditionalPermissionAdminImpl cpai,
- String name)
+ String name, boolean access)
{
+ m_allow = access;
m_name = name;
m_cpai = cpai;
m_conditions = CONDITION_INFO;
@@ -98,8 +101,10 @@
}
public ConditionalPermissionInfoImpl(ConditionInfo[] conditions,
- PermissionInfo[] permisions, ConditionalPermissionAdminImpl cpai)
+ PermissionInfo[] permisions, ConditionalPermissionAdminImpl cpai,
+ boolean access)
{
+ m_allow = access;
m_name = Long.toString(RANDOM.nextLong() ^ System.currentTimeMillis());
m_cpai = cpai;
m_conditions = conditions;
@@ -108,9 +113,11 @@
public ConditionalPermissionInfoImpl(String name,
ConditionInfo[] conditions, PermissionInfo[] permisions,
- ConditionalPermissionAdminImpl cpai)
+ ConditionalPermissionAdminImpl cpai, boolean access)
{
- m_name = name;
+ m_allow = access;
+ m_name = (name != null) ? name : Long.toString(RANDOM.nextLong()
+ ^ System.currentTimeMillis());
m_conditions = conditions;
m_permissions = permisions;
m_cpai = cpai;
@@ -182,6 +189,7 @@
public String getEncoded()
{
StringBuffer buffer = new StringBuffer();
+ buffer.append(m_allow ? "ALLOW " : "DENY ");
buffer.append('{');
buffer.append('\n');
buffer.append('#');
@@ -209,4 +217,15 @@
{
return getEncoded();
}
+
+ public String getAccessDecision()
+ {
+ return m_allow ? ConditionalPermissionInfo.ALLOW
+ : ConditionalPermissionInfo.DENY;
+ }
+
+ public boolean isAllow()
+ {
+ return m_allow;
+ }
}
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/DomainGripper.java b/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/DomainGripper.java
index ed0dd0c..c9c299f 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/DomainGripper.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/condpermadmin/DomainGripper.java
@@ -31,19 +31,19 @@
import org.apache.felix.framework.BundleProtectionDomain;
/**
- * This class is a hack to get all BundleProtectionDomains currently on the
+ * This class is a hack to get all BundleProtectionDomains currently on the
* security stack. This way we don't need to have our own security manager set.
*/
final class DomainGripper implements DomainCombiner, PrivilegedAction
{
- private static final ProtectionDomain[] ALL_PERMISSION_PD =
- new ProtectionDomain[] { new ProtectionDomain(null, null)
+ private static final ProtectionDomain[] ALL_PERMISSION_PD = new ProtectionDomain[] { new ProtectionDomain(
+ null, null)
+ {
+ public boolean implies(Permission perm)
{
- public boolean implies(Permission perm)
- {
- return true;
- }
- } };
+ return true;
+ }
+ } };
// A per thread cache of DomainGripper objects. We might want to wrap them
// in a softreference eventually
@@ -56,8 +56,8 @@
private AccessControlContext m_system = null;
/**
- * Get all bundle protection domains and add them to the m_domains. Then
- * return the ALL_PERMISSION_PD.
+ * Get all bundle protection domains and add them to the m_domains. Then
+ * return the ALL_PERMISSION_PD.
*/
public ProtectionDomain[] combine(ProtectionDomain[] current,
ProtectionDomain[] assigned)
@@ -84,7 +84,7 @@
}
/**
- * Get the current bundle protection domains on the stack up to the last
+ * Get the current bundle protection domains on the stack up to the last
* privileged call.
*/
public static List grab()
@@ -107,8 +107,8 @@
gripper.m_system = AccessController.getContext();
// and merge it with the current combiner (i.e., gripper)
- AccessControlContext context =
- (AccessControlContext) AccessController.doPrivileged(gripper);
+ AccessControlContext context = (AccessControlContext) AccessController
+ .doPrivileged(gripper);
gripper.m_system = null;
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/permissionadmin/PermissionAdminImpl.java b/framework.security/src/main/java/org/apache/felix/framework/security/permissionadmin/PermissionAdminImpl.java
index 5052f98..178358b 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/permissionadmin/PermissionAdminImpl.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/permissionadmin/PermissionAdminImpl.java
@@ -30,22 +30,23 @@
import org.apache.felix.framework.security.condpermadmin.ConditionalPermissionAdminImpl;
import org.apache.felix.framework.security.util.Permissions;
import org.apache.felix.framework.security.util.PropertiesCache;
+import org.apache.felix.moduleloader.IContent;
import org.osgi.framework.Bundle;
import org.osgi.service.permissionadmin.PermissionAdmin;
import org.osgi.service.permissionadmin.PermissionInfo;
/**
- * This class is a relatively straight forward implementation of the PermissionAdmin service.
- * The only somewhat involved thing is that it respects the presents of a
- * conditionalpermissionadmin service as per spec.
+ * This class is a relatively straight forward implementation of the
+ * PermissionAdmin service. The only somewhat involved thing is that it respects
+ * the presents of a conditionalpermissionadmin service as per spec.
*/
-// TODO: Do we need this class at all or can we just emulate it using the condpermadmin?
+// TODO: Do we need this class at all or can we just emulate it using the
+// condpermadmin?
public final class PermissionAdminImpl implements PermissionAdmin
{
- private static final PermissionInfo[] ALL_PERMISSION =
- new PermissionInfo[] { new PermissionInfo(
- AllPermission.class.getName(), "", "") };
-
+ private static final PermissionInfo[] ALL_PERMISSION = new PermissionInfo[] { new PermissionInfo(
+ AllPermission.class.getName(), "", "") };
+
private final Map m_store = new HashMap();
private final PropertiesCache m_cache;
@@ -59,11 +60,7 @@
{
m_permissions = permissions;
m_cache = cache;
- Map old = m_cache.read(PermissionInfo[].class);
- if (old != null)
- {
- m_store.putAll(old);
- }
+ m_cache.read(PermissionInfo[].class, m_store);
}
public PermissionInfo[] getDefaultPermissions()
@@ -106,71 +103,76 @@
}
/**
- * This will do the actual permission check as described in the core spec 10.2
- * It will respect a present condpermadmin service as described in 9.10.
+ * This will do the actual permission check as described in the core spec
+ * 10.2 It will respect a present condpermadmin service as described in
+ * 9.10.
*
- * @param location the location of the bundle.
- * @param bundle the bundle in question.
- * @param permission the permission to check.
- * @param cpai A condpermadmin if one is present else null.
- * @param pd the protectiondomain
- * @return Boolean.TRUE if the location is bound and the permission is
- * granted or if there is no cpa and the default permissions imply the
- * permission Boolean.FALSE otherwise unless the location is not bound and
- * their is a cpa in which case null is returned.
+ * @param location
+ * the location of the bundle.
+ * @param bundle
+ * the bundle in question.
+ * @param permission
+ * the permission to check.
+ * @param cpai
+ * A condpermadmin if one is present else null.
+ * @param pd
+ * the protectiondomain
+ * @return Boolean.TRUE if the location is bound and the permission is
+ * granted or if there is no cpa and the default permissions imply
+ * the permission Boolean.FALSE otherwise unless the location is not
+ * bound and their is a cpa in which case null is returned.
*/
public Boolean hasPermission(String location, Bundle bundle,
Permission permission, ConditionalPermissionAdminImpl cpai,
- ProtectionDomain pd)
+ ProtectionDomain pd, IContent content)
{
PermissionInfo[] permissions = null;
- boolean file = false;
+ PermissionInfo[] defaults = null;
+ boolean contains = false;
synchronized (m_store)
{
- if (m_store.containsKey(location))
- {
- permissions = (PermissionInfo[]) m_store.get(location);
- file = true;
- }
- else if ((cpai == null) || (cpai.isEmpty()))
- {
- if (m_default != null)
- {
- permissions = m_default;
- }
- else
- {
- permissions = ALL_PERMISSION;
- }
- }
+ contains = m_store.containsKey(location);
+ permissions = (PermissionInfo[]) m_store.get(location);
+ defaults = m_default;
}
- if ((cpai == null) || cpai.isEmpty() || file)
+ if (contains)
{
- if (check(permissions, permission, file ? bundle : null))
+ if (check(permissions, permission, bundle))
+ {
+ return Boolean.TRUE;
+ }
+ return check(m_permissions.getImplicit(bundle), permission, bundle) ? Boolean.TRUE
+ : Boolean.FALSE;
+ }
+ else if (cpai == null
+ || (cpai.isEmpty() && cpai
+ .impliesLocal(bundle, content, permission)))
+ {
+ if (defaults != null)
+ {
+ if (check(defaults, permission, null))
+ {
+ return Boolean.TRUE;
+ }
+ return check(m_permissions.getImplicit(bundle), permission,
+ bundle) ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else
{
return Boolean.TRUE;
}
}
-
- permissions = m_permissions.getImplicit(bundle);
-
- if (check(permissions, permission, bundle))
- {
- return Boolean.TRUE;
- }
-
- if ((cpai != null) && !file)
+ else
{
return null;
}
- return Boolean.FALSE;
}
private boolean check(PermissionInfo[] permissions, Permission permission,
Bundle bundle)
{
- Permissions permissionsObject =
- m_permissions.getPermissions(permissions);
+ Permissions permissionsObject = m_permissions
+ .getPermissions(permissions);
return permissionsObject.implies(permission, bundle);
}
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/util/BundleInputStream.java b/framework.security/src/main/java/org/apache/felix/framework/security/util/BundleInputStream.java
index 1e3f0cc..2dd3b18 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/util/BundleInputStream.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/util/BundleInputStream.java
@@ -33,27 +33,27 @@
import org.apache.felix.moduleloader.IContent;
/**
- * This class makes a given content available as a inputstream with a jar content.
- * In other words the stream can be used as input to a JarInputStream.
+ * This class makes a given content available as a inputstream with a jar
+ * content. In other words the stream can be used as input to a JarInputStream.
*/
public final class BundleInputStream extends InputStream
{
private final IContent m_root;
private final Enumeration m_content;
private final OutputStreamBuffer m_outputBuffer = new OutputStreamBuffer();
-
+
private ByteArrayInputStream m_buffer = null;
private JarOutputStream m_output = null;
-
- public BundleInputStream(IContent root) throws IOException
+
+ public BundleInputStream(IContent root) throws IOException
{
m_root = root;
-
+
List entries = new ArrayList();
-
+
int count = 0;
String manifest = null;
- for (Enumeration e = m_root.getEntries();e.hasMoreElements();)
+ for (Enumeration e = m_root.getEntries(); e.hasMoreElements();)
{
String entry = (String) e.nextElement();
if (entry.equalsIgnoreCase("META-INF/MANIFEST.MF"))
@@ -77,16 +77,16 @@
manifest = "META-INF/MANIFEST.MF";
}
m_content = new IteratorToEnumeration(entries.iterator());
-
+
try
{
m_output = new JarOutputStream(m_outputBuffer);
readNext(manifest);
- m_buffer = new ByteArrayInputStream(
- m_outputBuffer.m_outBuffer.toByteArray());
+ m_buffer = new ByteArrayInputStream(m_outputBuffer.m_outBuffer
+ .toByteArray());
m_outputBuffer.m_outBuffer = null;
- }
+ }
catch (IOException ex)
{
// TODO: figure out what is wrong
@@ -111,24 +111,24 @@
m_buffer = null;
return read();
}
-
+
return result;
}
if (m_content.hasMoreElements())
{
String current = (String) m_content.nextElement();
-
+
readNext(current);
-
+
if (!m_content.hasMoreElements())
{
m_output.close();
m_output = null;
}
- m_buffer = new ByteArrayInputStream(
- m_outputBuffer.m_outBuffer.toByteArray());
+ m_buffer = new ByteArrayInputStream(m_outputBuffer.m_outBuffer
+ .toByteArray());
m_outputBuffer.m_outBuffer = null;
}
@@ -149,7 +149,7 @@
{
throw new IOException("Missing entry");
}
-
+
JarEntry entry = new JarEntry(path);
m_output.putNextEntry(entry);
@@ -169,7 +169,7 @@
{
in.close();
}
- catch (Exception ex)
+ catch (Exception ex)
{
// Not much we can do
}
@@ -180,7 +180,7 @@
m_output.flush();
}
-
+
private static final class OutputStreamBuffer extends OutputStream
{
ByteArrayOutputStream m_outBuffer = null;
@@ -189,12 +189,12 @@
{
m_outBuffer.write(b);
}
-
+
public void write(byte[] buffer) throws IOException
{
m_outBuffer.write(buffer);
- }
-
+ }
+
public void write(byte[] buffer, int offset, int length)
{
m_outBuffer.write(buffer, offset, length);
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/util/Conditions.java b/framework.security/src/main/java/org/apache/felix/framework/security/util/Conditions.java
index ec1375f..1679fe1 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/util/Conditions.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/util/Conditions.java
@@ -18,21 +18,20 @@
*/
package org.apache.felix.framework.security.util;
-import java.util.ArrayList;
+import java.security.Permission;
+import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.WeakHashMap;
-import java.util.Map.Entry;
-import org.apache.felix.framework.security.verifier.SignerMatcher;
+import org.apache.felix.framework.security.condpermadmin.ConditionalPermissionInfoImpl;
import org.apache.felix.framework.util.SecureAction;
+import org.apache.felix.moduleloader.IModule;
import org.osgi.framework.Bundle;
-import org.osgi.service.condpermadmin.BundleSignerCondition;
import org.osgi.service.condpermadmin.Condition;
import org.osgi.service.condpermadmin.ConditionInfo;
@@ -44,11 +43,11 @@
public final class Conditions
{
private static final ThreadLocal m_conditionStack = new ThreadLocal();
+ private static final Map m_conditionCache = new WeakHashMap();
private final Map m_cache = new WeakHashMap();
-
- private final Bundle m_bundle;
- private final String[] m_signers;
+
+ private final IModule m_module;
private final ConditionInfo[] m_conditionInfos;
private final Condition[] m_conditions;
@@ -56,21 +55,44 @@
public Conditions(SecureAction action)
{
- this(null, null, null, action);
+ this(null, null, action);
}
-
- private Conditions(Bundle bundle, String[] signers,
- ConditionInfo[] conditions, SecureAction action)
+
+ private Conditions(IModule module, ConditionInfo[] conditionInfos,
+ SecureAction action)
{
- m_bundle = bundle;
- m_signers = signers;
- m_conditionInfos = conditions;
- m_conditions = ((conditions != null) && (bundle != null)) ? new Condition[m_conditionInfos.length] : null;
+ m_module = module;
+ m_conditionInfos = conditionInfos;
+ if ((module != null) && (conditionInfos != null))
+ {
+ synchronized (m_conditionCache)
+ {
+ Map conditionMap = (Map) m_conditionCache.get(module);
+ if (conditionMap == null)
+ {
+ conditionMap = new HashMap();
+ conditionMap.put(m_conditionInfos,
+ new Condition[m_conditionInfos.length]);
+ m_conditionCache.put(module, conditionMap);
+ }
+ Condition[] conditions = (Condition[]) conditionMap
+ .get(m_conditionInfos);
+ if (conditions == null)
+ {
+ conditions = new Condition[m_conditionInfos.length];
+ conditionMap.put(m_conditionInfos, conditions);
+ }
+ m_conditions = conditions;
+ }
+ }
+ else
+ {
+ m_conditions = null;
+ }
m_action = action;
}
- public Conditions getConditions(Bundle bundle, String[] signers,
- ConditionInfo[] conditions)
+ public Conditions getConditions(IModule key, ConditionInfo[] conditions)
{
Conditions result = null;
Map index = null;
@@ -85,90 +107,85 @@
}
synchronized (index)
{
- if (bundle != null)
+ if (key != null)
{
- result = (Conditions) index.get(bundle);
+ result = (Conditions) index.get(key);
}
}
-
+
if (result == null)
{
- result = new Conditions(bundle, signers, conditions, m_action);
+ result = new Conditions(key, conditions, m_action);
synchronized (index)
{
- index.put(bundle, result);
+ index.put(key, result);
}
}
-
+
return result;
}
// See whether the given list is satisfied or not
- public boolean isSatisfied(List posts)
+ public boolean isSatisfied(List posts, Permissions permissions,
+ Permission permission)
{
- for (int i = 0; i < m_conditions.length; i++)
+ boolean check = true;
+ for (int i = 0; i < m_conditionInfos.length; i++)
{
- if (m_bundle == null)
+ if (m_module == null)
{
- if (!m_conditionInfos[i].getType().equals(
- BundleSignerCondition.class.getName()))
- {
- return false;
- }
- String[] args = m_conditionInfos[i].getArgs();
-
- boolean match = false;
- if (args.length == 0)
- {
- for (int j = 0; j < m_signers.length; j++)
- {
- if (SignerMatcher.match(args[0], m_signers[j]))
- {
- match = true;
- break;
- }
- }
- }
- if (!match)
- {
- return false;
- }
- continue;
+ // TODO: check whether this is correct!
+ break;
}
try
{
Condition condition = null;
boolean add = false;
Class clazz = Class.forName(m_conditionInfos[i].getType());
-
- synchronized (m_conditionInfos)
+
+ synchronized (m_conditions)
{
+ if (m_conditions[i] == null)
+ {
+ m_conditions[i] = createCondition(m_module.getBundle(),
+ clazz, m_conditionInfos[i]);
+ }
condition = m_conditions[i];
}
-
- if (condition == null)
+
+ Object current = m_conditionStack.get();
+ if (current != null)
{
- add = true;
- condition = createCondition(m_bundle, clazz, m_conditionInfos[i]);
- }
-
- if (condition.isPostponed())
- {
- posts.add(condition);
- if (add)
+ if (current instanceof HashSet)
{
- synchronized (m_conditionInfos)
+ if (((HashSet) current).contains(clazz))
{
- if (m_conditions[i] == null)
- {
- m_conditions[i] = condition;
- }
+ return false;
+ }
+ }
+ else
+ {
+ if (current == clazz)
+ {
+ return false;
}
}
}
+
+ if (condition.isPostponed())
+ {
+ if (check && !permissions.implies(permission, null))
+ {
+ return false;
+ }
+ else
+ {
+ check = false;
+ }
+ posts.add(new Object[] { condition, new Integer(i) });
+ }
else
{
- Object current = m_conditionStack.get();
if (current == null)
{
@@ -199,20 +216,16 @@
}
try
{
+ boolean mutable = condition.isMutable();
boolean result = condition.isSatisfied();
- if (!condition.isMutable() && ((condition != Condition.TRUE) && (condition != Condition.FALSE)))
+ if (!mutable
+ && ((condition != Condition.TRUE) && (condition != Condition.FALSE)))
{
- synchronized (m_conditionInfos)
+ synchronized (m_conditions)
{
- m_conditions[i] = result ? Condition.TRUE : Condition.FALSE;
- }
- }
- else
- {
- synchronized (m_conditionInfos)
- {
- m_conditions[i] = condition;
+ m_conditions[i] = result ? Condition.TRUE
+ : Condition.FALSE;
}
}
if (!result)
@@ -249,121 +262,99 @@
public boolean evalRecursive(List entries)
{
- return _evalRecursive(entries, 0, new ArrayList(), new HashMap());
- }
-
- private boolean _evalRecursive(List entries, int pos, List acc, Map contexts)
- {
- if (pos == entries.size())
+ Map contexts = new HashMap();
+ outer: for (Iterator iter = entries.iterator(); iter.hasNext();)
{
- // we need to group by type by tuple
- Map conditions = new HashMap();
- for (Iterator iter = acc.iterator(); iter.hasNext();)
+ List tuples = (List) iter.next();
+ inner: for (Iterator inner = tuples.iterator(); inner.hasNext();)
{
- for (Iterator iter2 = ((List) iter.next()).iterator(); iter2
- .hasNext();)
+ Object[] entry = (Object[]) inner.next();
+ List conditions = (List) entry[1];
+ if (conditions == null)
{
- Object entry = iter2.next();
- Set group = (Set) conditions.get(entry.getClass());
-
- if (group == null)
- {
- group = new HashSet();
- }
- group.add(entry);
-
- conditions.put(entry.getClass(), group);
- }
- }
-
- // and then eval per group
- for (Iterator iter = conditions.entrySet().iterator(); iter.hasNext();)
- {
- Entry entry = (Entry) iter.next();
- Class key = (Class) entry.getKey();
-
- Hashtable context = (Hashtable) contexts.get(key);
- if (context == null)
- {
- context = new Hashtable();
- contexts.put(key, context);
- }
- Set set = (Set) entry.getValue();
- Condition[] current =
- (Condition[]) set.toArray(new Condition[set.size()]);
-
- // We must be catching recursive evaluation as per spec, hence use a thread
- // local stack to do so
- Object currentCond = m_conditionStack.get();
-
- if (currentCond == null)
- {
- m_conditionStack.set(key);
- }
- else
- {
- if (currentCond instanceof HashSet)
- {
- if (((HashSet) currentCond).contains(key))
- {
- return false;
- }
- ((HashSet) currentCond).add(key);
- }
- else
- {
- if (currentCond == key)
- {
- return false;
- }
- HashSet frame = new HashSet();
- frame.add(current);
- frame.add(key);
- m_conditionStack.set(frame);
- currentCond = frame;
- }
- }
- try
- {
- if (!current[0].isSatisfied(current, context))
+ if (!((ConditionalPermissionInfoImpl) entry[0]).isAllow())
{
return false;
}
+ continue outer;
}
- finally
+ for (Iterator iter2 = conditions.iterator(); iter2.hasNext();)
{
- if (currentCond == null)
+ Object[] condEntry = (Object[]) iter2.next();
+ Condition cond = (Condition) condEntry[0];
+ Dictionary context = (Dictionary) contexts.get(cond
+ .getClass());
+ if (context == null)
{
- m_conditionStack.set(null);
+ context = new Hashtable();
+ contexts.put(cond.getClass(), context);
+ }
+ Object current = m_conditionStack.get();
+ if (current == null)
+ {
+ m_conditionStack.set(cond.getClass());
}
else
{
- ((HashSet) currentCond).remove(key);
- if (((HashSet) currentCond).isEmpty())
+ if (current instanceof HashSet)
+ {
+ ((HashSet) current).add(cond.getClass());
+ }
+ else
+ {
+ HashSet frame = new HashSet();
+ frame.add(current);
+ frame.add(cond.getClass());
+ m_conditionStack.set(frame);
+ current = frame;
+ }
+ }
+ boolean result;
+ boolean mutable = cond.isMutable();
+ try
+ {
+ result = cond.isSatisfied(new Condition[] { cond },
+ context);
+ }
+ finally
+ {
+ if (current == null)
{
m_conditionStack.set(null);
}
+ else
+ {
+ ((HashSet) current).remove(cond.getClass());
+ if (((HashSet) current).isEmpty())
+ {
+ m_conditionStack.set(null);
+ }
+ }
+ }
+ if (!mutable && (cond != Condition.TRUE)
+ && (cond != Condition.FALSE))
+ {
+ synchronized (((Conditions) entry[2]).m_conditions)
+ {
+ ((Conditions) entry[2]).m_conditions[((Integer) condEntry[1])
+ .intValue()] = result ? Condition.TRUE
+ : Condition.FALSE;
+ }
+ }
+ if (!result)
+ {
+ continue inner;
}
}
+ if (!((ConditionalPermissionInfoImpl) entry[0]).isAllow())
+ {
+ return false;
+ }
+ continue outer;
}
- return true;
+ return false;
}
-
- List entry = (List) entries.get(pos);
-
- for (int i = 0; i < entry.size(); i++)
- {
- acc.add(entry.get(i));
-
- if (_evalRecursive(entries, pos + 1, acc, contexts))
- {
- return true;
- }
-
- acc.remove(acc.size() - 1);
- }
-
- return false;
+ return true;
}
private Condition createCondition(final Bundle bundle, final Class clazz,
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/util/LocalPermissions.java b/framework.security/src/main/java/org/apache/felix/framework/security/util/LocalPermissions.java
index 5f31205..a926338 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/util/LocalPermissions.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/util/LocalPermissions.java
@@ -25,13 +25,9 @@
import java.security.AllPermission;
import java.security.Permission;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
-import java.util.Map.Entry;
+import java.util.WeakHashMap;
-import org.apache.felix.framework.security.util.Permissions;
-import org.apache.felix.framework.security.util.PropertiesCache;
import org.apache.felix.moduleloader.IContent;
import org.osgi.framework.Bundle;
import org.osgi.service.permissionadmin.PermissionInfo;
@@ -43,31 +39,15 @@
// TODO: maybe use bundle events to clean thing up or weak/soft references
public final class LocalPermissions
{
- private static final PermissionInfo[] ALL_PERMISSION =
- new PermissionInfo[] { new PermissionInfo(
- AllPermission.class.getName(), "", "") };
-
- private final Map m_cache = new HashMap();
+ private static final PermissionInfo[] ALL_PERMISSION = new PermissionInfo[] { new PermissionInfo(
+ AllPermission.class.getName(), "", "") };
+
+ private final Map m_cache = new WeakHashMap();
private final Permissions m_permissions;
- public LocalPermissions(Permissions permissions, PropertiesCache cache)
- throws IOException
+ public LocalPermissions(Permissions permissions) throws IOException
{
m_permissions = permissions;
- for (Iterator iter =
- cache.read(PermissionInfo[].class).entrySet().iterator(); iter
- .hasNext();)
- {
- Entry entry = (Entry) iter.next();
- PermissionInfo[] value = (PermissionInfo[]) entry.getValue();
- if ((value.length == 1)
- && (AllPermission.class.getName().equals(value[0].getType())))
- {
- value = ALL_PERMISSION;
- }
-
- m_cache.put(entry.getKey(), value);
- }
}
/**
@@ -75,20 +55,24 @@
* permissions of the given bundle or if there are none otherwise, false.
* See core spec 9.2.1.
*
- * @param root the root to use for cacheing as a key
- * @param loader the loader to get the content of the bundle from
- * @param bundle the bundle in quesiton
- * @param permission the permission to check
+ * @param root
+ * the root to use for cacheing as a key
+ * @param loader
+ * the loader to get the content of the bundle from
+ * @param bundle
+ * the bundle in quesiton
+ * @param permission
+ * the permission to check
* @return true if implied by local permissions.
*/
- public boolean implies(String root, IContent content, Bundle bundle,
+ public boolean implies(IContent content, Bundle bundle,
Permission permission)
{
PermissionInfo[] permissions = null;
synchronized (m_cache)
{
- if (!m_cache.containsKey(root))
+ if (!m_cache.containsKey(content))
{
InputStream in = null;
try
@@ -98,28 +82,26 @@
{
ArrayList perms = new ArrayList();
- BufferedReader reader =
- new BufferedReader(new InputStreamReader(in,
- "UTF-8"));
- for (String line = reader.readLine(); line != null; line =
- reader.readLine())
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(in, "UTF-8"));
+ for (String line = reader.readLine(); line != null; line = reader
+ .readLine())
{
String trim = line.trim();
- if (trim.startsWith("#") || trim.startsWith("//"))
+ if (trim.startsWith("#") || trim.startsWith("//")
+ || (trim.length() == 0))
{
continue;
}
perms.add(new PermissionInfo(line));
}
- permissions =
- (PermissionInfo[]) perms
- .toArray(new PermissionInfo[perms.size()]);
+ permissions = (PermissionInfo[]) perms
+ .toArray(new PermissionInfo[perms.size()]);
}
}
catch (Exception ex)
{
- ex.printStackTrace();
}
finally
{
@@ -142,23 +124,15 @@
permissions = ALL_PERMISSION;
}
- m_cache.put(root, permissions);
+ m_cache.put(content, permissions);
}
else
{
- permissions = (PermissionInfo[]) m_cache.get(root);
+ permissions = (PermissionInfo[]) m_cache.get(content);
}
}
return m_permissions.getPermissions(permissions).implies(permission,
bundle);
}
-
- public Map getStore()
- {
- synchronized (m_cache)
- {
- return new HashMap(m_cache);
- }
- }
}
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/util/Permissions.java b/framework.security/src/main/java/org/apache/felix/framework/security/util/Permissions.java
index 5ce3ca5..5d885cf 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/util/Permissions.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/util/Permissions.java
@@ -23,10 +23,11 @@
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
+import java.security.AccessController;
import java.security.AllPermission;
import java.security.Permission;
import java.security.PermissionCollection;
-import java.security.*;
+import java.security.PrivilegedAction;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
@@ -44,19 +45,18 @@
import org.osgi.service.permissionadmin.PermissionInfo;
/**
- * A permission cache that uses permisssion infos as keys. Permission are created
- * from the parent classloader or any exported package.
+ * A permission cache that uses permisssion infos as keys. Permission are
+ * created from the parent classloader or any exported package.
*/
// TODO: maybe use bundle events instead of soft/weak references
public final class Permissions
{
- private static final ClassLoader m_classLoader =
- Permissions.class.getClassLoader();
+ private static final ClassLoader m_classLoader = Permissions.class
+ .getClassLoader();
private static final Map m_permissionCache = new HashMap();
private static final Map m_permissions = new HashMap();
- private static final ReferenceQueue m_permissionsQueue =
- new ReferenceQueue();
+ private static final ReferenceQueue m_permissionsQueue = new ReferenceQueue();
private static final ThreadLocal m_stack = new ThreadLocal();
@@ -69,9 +69,8 @@
public static final AllPermission ALL_PERMISSION = new AllPermission();
- private static final PermissionInfo[] IMPLICIT =
- new PermissionInfo[] { new PermissionInfo(FilePermission.class
- .getName(), "-", "read,write,delete") };
+ private static final PermissionInfo[] IMPLICIT = new PermissionInfo[] { new PermissionInfo(
+ FilePermission.class.getName(), "-", "read,write,delete") };
Permissions(PermissionInfo[] permissionInfos, BundleContext context,
SecureAction action)
@@ -102,13 +101,17 @@
m_allPermission = true;
m_action = action;
}
-
+
public PermissionInfo[] getImplicit(Bundle bundle)
{
return new PermissionInfo[] {
IMPLICIT[0],
new PermissionInfo(AdminPermission.class.getName(), "(id="
- + bundle.getBundleId() + ")", AdminPermission.METADATA) };
+ + bundle.getBundleId() + ")", AdminPermission.METADATA),
+ new PermissionInfo(AdminPermission.class.getName(), "(id="
+ + bundle.getBundleId() + ")", AdminPermission.RESOURCE),
+ new PermissionInfo(AdminPermission.class.getName(), "(id="
+ + bundle.getBundleId() + ")", AdminPermission.CONTEXT) };
}
public Permissions getPermissions(PermissionInfo[] permissionInfos)
@@ -177,11 +180,11 @@
return false;
}
- if (o instanceof Entry)
+ if (o instanceof Entry)
{
- return entry.equals(((Entry)o).get());
- }
- else
+ return entry.equals(((Entry) o).get());
+ }
+ else
{
return false;
}
@@ -237,8 +240,8 @@
private void cleanUp(ReferenceQueue queue, Map cache)
{
- for (Entry entry = (Entry) queue.poll(); entry != null; entry =
- (Entry) queue.poll())
+ for (Entry entry = (Entry) queue.poll(); entry != null; entry = (Entry) queue
+ .poll())
{
synchronized (cache)
{
@@ -248,12 +251,14 @@
}
/**
- * @param target the permission to be implied
- * @param bundle if not null then allow implicit permissions like file
- * access to local data area
+ * @param target
+ * the permission to be implied
+ * @param bundle
+ * if not null then allow implicit permissions like file access
+ * to local data area
* @return true if the permission is implied by this permissions object.
*/
- public boolean implies(Permission target, Bundle bundle)
+ public boolean implies(Permission target, final Bundle bundle)
{
if (m_allPermission)
{
@@ -273,7 +278,7 @@
{
String postfix = "";
String name = m_permissionInfos[i].getName();
- if (!"<<ALL FILES>>".equals(name))
+ if (!"<<ALL FILES>>".equals(name))
{
if (name.endsWith("*") || name.endsWith("-"))
{
@@ -282,13 +287,20 @@
}
if (!(new File(name)).isAbsolute())
{
- BundleContext context = bundle.getBundleContext();
+ BundleContext context = (BundleContext) AccessController
+ .doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return bundle.getBundleContext();
+ }
+ });
if (context == null)
{
break;
}
- name =
- m_action.getAbsolutePath(new File(context.getDataFile(""), name));
+ name = m_action.getAbsolutePath(new File(context
+ .getDataFile(""), name));
}
if (postfix.length() > 0)
{
@@ -302,10 +314,54 @@
}
}
}
- return createPermission(
+ Permission source = createPermission(new PermissionInfo(
+ FilePermission.class.getName(), name,
+ m_permissionInfos[i].getActions()), targetClass);
+ postfix = "";
+ name = target.getName();
+ if (!"<<ALL FILES>>".equals(name))
+ {
+ if (name.endsWith("*") || name.endsWith("-"))
+ {
+ postfix = name.substring(name.length() - 1);
+ name = name.substring(0, name.length() - 1);
+ }
+ if (!(new File(name)).isAbsolute())
+ {
+ BundleContext context = (BundleContext) AccessController
+ .doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return bundle.getBundleContext();
+ }
+ });
+ if (context == null)
+ {
+ break;
+ }
+ name = m_action.getAbsolutePath(new File(context
+ .getDataFile(""), name));
+ }
+ if (postfix.length() > 0)
+ {
+ if ((name.length() > 0) && !name.endsWith("/"))
+ {
+ name += "/" + postfix;
+ }
+ else
+ {
+ name += postfix;
+ }
+ }
+ }
+ Permission realTarget = createPermission(
new PermissionInfo(FilePermission.class.getName(),
- name, m_permissionInfos[i].getActions()),
- targetClass).implies(target);
+ name, target.getActions()), targetClass);
+ if (source.implies(realTarget))
+ {
+ return true;
+ }
}
}
return false;
@@ -374,8 +430,8 @@
if (infoType.equals(permissionType))
{
- Permission permission =
- createPermission(permissionInfo, targetClass);
+ Permission permission = createPermission(
+ permissionInfo, targetClass);
if (permission != null)
{
@@ -478,86 +534,94 @@
private Permission createPermission(final PermissionInfo permissionInfo,
final Class target)
{
- return (Permission) AccessController.doPrivileged(new PrivilegedAction() {
- public Object run()
+ return (Permission) AccessController
+ .doPrivileged(new PrivilegedAction()
{
- Permission cached = getFromCache(permissionInfo.getEncoded(), target);
-
- if (cached != null)
- {
- return cached;
- }
-
- try
- {
- if (m_classLoader.loadClass(target.getName()) == target)
- {
- return addToCache(permissionInfo.getEncoded(),
- createPermission(permissionInfo.getName(), permissionInfo
- .getActions(), target));
- }
- }
- catch (ClassNotFoundException e1)
- {
- }
-
- ServiceReference[] refs = null;
- try
- {
- refs =
- m_context.getServiceReferences(PackageAdmin.class.getName(),
- null);
- }
- catch (InvalidSyntaxException e)
- {
- }
- if (refs != null)
- {
- for (int i = 0; i < refs.length; i++)
- {
- PackageAdmin admin =
- (PackageAdmin) m_context.getService(refs[i]);
-
- if (admin != null)
+ public Object run()
{
- Permission result = null;
- Bundle bundle = admin.getBundle(target);
- if (bundle != null)
- {
- ExportedPackage[] exports =
- admin.getExportedPackages(bundle);
- if (exports != null)
- {
- String name = target.getName();
- name = name.substring(0, name.lastIndexOf('.'));
+ Permission cached = getFromCache(permissionInfo
+ .getEncoded(), target);
- for (int j = 0; j < exports.length; j++)
+ if (cached != null)
+ {
+ return cached;
+ }
+
+ try
+ {
+ if (m_classLoader.loadClass(target.getName()) == target)
+ {
+ return addToCache(permissionInfo.getEncoded(),
+ createPermission(permissionInfo.getName(),
+ permissionInfo.getActions(), target));
+ }
+ }
+ catch (ClassNotFoundException e1)
+ {
+ }
+
+ ServiceReference[] refs = null;
+ try
+ {
+ refs = m_context.getServiceReferences(
+ PackageAdmin.class.getName(), null);
+ }
+ catch (InvalidSyntaxException e)
+ {
+ }
+ if (refs != null)
+ {
+ for (int i = 0; i < refs.length; i++)
+ {
+ PackageAdmin admin = (PackageAdmin) m_context
+ .getService(refs[i]);
+
+ if (admin != null)
{
- if (exports[j].getName().equals(name))
+ Permission result = null;
+ Bundle bundle = admin.getBundle(target);
+ if (bundle != null)
{
- result =
- createPermission(permissionInfo
- .getName(), permissionInfo
- .getActions(), target);
- break;
+ ExportedPackage[] exports = admin
+ .getExportedPackages(bundle);
+ if (exports != null)
+ {
+ String name = target.getName();
+ name = name.substring(0, name
+ .lastIndexOf('.'));
+
+ for (int j = 0; j < exports.length; j++)
+ {
+ if (exports[j].getName().equals(
+ name))
+ {
+ result = createPermission(
+ permissionInfo.getName(),
+ permissionInfo.getActions(),
+ target);
+ break;
+ }
+ }
+ }
}
+
+ m_context.ungetService(refs[i]);
+
+ return addToCache(permissionInfo.getEncoded(),
+ result);
}
}
}
- m_context.ungetService(refs[i]);
-
- return addToCache(permissionInfo.getEncoded(), result);
+ return null;
}
- }
- }
-
- return null;
- }});
+ });
}
private Permission createPermission(String name, String action, Class target)
{
+ // System.out.println("\n\n|" + name + "|\n--\n|" + action + "|\n--\n" +
+ // target + "\n\n");
try
{
return (Permission) m_action.getConstructor(target,
@@ -566,7 +630,7 @@
}
catch (Exception ex)
{
- ex.printStackTrace();
+ // TODO: log this or something
}
return null;
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/util/PropertiesCache.java b/framework.security/src/main/java/org/apache/felix/framework/security/util/PropertiesCache.java
index c00bb8c..6c76d39 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/util/PropertiesCache.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/util/PropertiesCache.java
@@ -26,10 +26,10 @@
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
+import java.util.TreeMap;
import java.util.Map.Entry;
import org.apache.felix.framework.util.SecureAction;
@@ -66,11 +66,13 @@
Properties store = new Properties();
+ int count = 0;
+
for (Iterator iter = data.entrySet().iterator(); iter.hasNext();)
{
Entry entry = (Entry) iter.next();
- store.setProperty((String) entry.getKey(), getEncoded(entry
- .getValue()));
+ store.setProperty(count++ + "-" + (String) entry.getKey(),
+ getEncoded(entry.getValue()));
}
store.store(out, null);
@@ -126,15 +128,15 @@
}
}
- public Map read(Class target) throws IOException
+ public void read(Class target, Map map) throws IOException
{
if (!m_file.isFile())
{
- return null;
+ return;
}
InputStream in = null;
Exception other = null;
- Map result = new HashMap();
+ Map result = new TreeMap();
try
{
in = m_action.getFileInputStream(m_file);
@@ -171,7 +173,12 @@
}
}
}
- return result;
+ for (Iterator iter = result.entrySet().iterator(); iter.hasNext();)
+ {
+ Entry entry = (Entry) iter.next();
+ String key = (String) entry.getKey();
+ map.put(key.substring(key.indexOf("-")), entry.getValue());
+ }
}
private String getEncoded(Object target) throws IOException
@@ -204,26 +211,25 @@
Properties props = new Properties();
props.load(new ByteArrayInputStream(encoded.getBytes()));
Class componentType = target.getComponentType();
- Constructor constructor =
- m_action.getConstructor(componentType,
- new Class[] { String.class });
+ Constructor constructor = m_action.getConstructor(
+ componentType, new Class[] { String.class });
Object[] params = new Object[1];
- Object[] result =
- (Object[]) Array.newInstance(componentType, props.size());
+ Object[] result = (Object[]) Array.newInstance(componentType,
+ props.size());
for (Iterator iter = props.entrySet().iterator(); iter
.hasNext();)
{
Entry entry = (Entry) iter.next();
params[0] = entry.getValue();
- result[Integer.parseInt((String) entry.getKey())] =
- constructor.newInstance(params);
+ result[Integer.parseInt((String) entry.getKey())] = constructor
+ .newInstance(params);
}
return result;
}
- return m_action.invoke(m_action.getConstructor(target,
+ return m_action.invoke(m_action.getConstructor(target,
new Class[] { String.class }), new Object[] { encoded });
}
catch (Exception ex)
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/util/TrustManager.java b/framework.security/src/main/java/org/apache/felix/framework/security/util/TrustManager.java
index 7425de5..4633cb0 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/util/TrustManager.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/util/TrustManager.java
@@ -78,19 +78,17 @@
if (m_crlList.trim().length() != 0)
{
- CertificateFactory fac =
- CertificateFactory.getInstance("X509");
-
+ CertificateFactory fac = CertificateFactory.getInstance("X509");
+
for (StringTokenizer tok = new StringTokenizer(m_crlList, "|"); tok
.hasMoreElements();)
{
InputStream input = null;
try
{
- input =
- m_action.getURLConnectionInputStream(m_action
- .createURL(null, tok.nextToken(), null)
- .openConnection());
+ input = m_action.getURLConnectionInputStream(m_action
+ .createURL(null, tok.nextToken(), null)
+ .openConnection());
result.addAll(fac.generateCRLs(input));
}
catch (Exception ex)
@@ -137,20 +135,18 @@
InputStream input = null;
try
{
- input =
- m_action.getURLConnectionInputStream(m_action
- .createURL(null, storeTok.nextToken().trim(), null)
- .openConnection());
+ input = m_action.getURLConnectionInputStream(m_action
+ .createURL(null, storeTok.nextToken().trim(), null)
+ .openConnection());
+ String pass = passwdTok.nextToken().trim();
- ks.load(input, passwdTok.nextToken().trim().toCharArray());
+ ks.load(input, (pass.length() > 0) ? pass.toCharArray()
+ : null);
for (Enumeration e = ks.aliases(); e.hasMoreElements();)
{
String alias = (String) e.nextElement();
- if (ks.isCertificateEntry(alias))
- {
- result.add(ks.getCertificate(alias));
- }
+ result.add(ks.getCertificate(alias));
}
}
catch (Exception ex)
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/verifier/BundleDNParser.java b/framework.security/src/main/java/org/apache/felix/framework/security/verifier/BundleDNParser.java
index ef9f5d4..e237f61 100644
--- a/framework.security/src/main/java/org/apache/felix/framework/security/verifier/BundleDNParser.java
+++ b/framework.security/src/main/java/org/apache/felix/framework/security/verifier/BundleDNParser.java
@@ -30,13 +30,15 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.WeakHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
-import org.apache.felix.framework.cache.BundleRevision;
import org.apache.felix.framework.security.util.BundleInputStream;
import org.apache.felix.framework.security.util.TrustManager;
import org.apache.felix.moduleloader.IContent;
+import org.apache.felix.moduleloader.IModule;
+import org.osgi.framework.Bundle;
public final class BundleDNParser
{
@@ -51,15 +53,12 @@
Method getCertificates = null;
try
{
- getCodeSigners =
- Class.forName("java.util.jar.JarEntry").getMethod(
- "getCodeSigners", null);
- getSignerCertPath =
- Class.forName("java.security.CodeSigner").getMethod(
- "getSignerCertPath", null);
- getCertificates =
- Class.forName("java.security.cert.CertPath").getMethod(
- "getCertificates", null);
+ getCodeSigners = Class.forName("java.util.jar.JarEntry").getMethod(
+ "getCodeSigners", null);
+ getSignerCertPath = Class.forName("java.security.CodeSigner")
+ .getMethod("getSignerCertPath", null);
+ getCertificates = Class.forName("java.security.cert.CertPath")
+ .getMethod("getCertificates", null);
}
catch (Exception ex)
{
@@ -73,7 +72,9 @@
m_getCertificates = getCertificates;
}
- private final Map m_cache = new HashMap();
+ private final Map m_cache = new WeakHashMap();
+ private final Map m_allCache = new WeakHashMap();
+
private final TrustManager m_manager;
public BundleDNParser(TrustManager manager)
@@ -89,7 +90,7 @@
}
}
- public void put(String root, String[] dnChains)
+ public void put(String root, X509Certificate[] dnChains)
{
synchronized (m_cache)
{
@@ -97,35 +98,65 @@
}
}
- public void checkDNChains(String root, IContent content) throws Exception
+ public void checkDNChains(IModule root, IContent content, int signersType)
+ throws Exception
{
- synchronized (m_cache)
+ if (signersType == Bundle.SIGNERS_TRUSTED)
{
- if (m_cache.containsKey(root))
+ synchronized (m_cache)
{
- String[] result = (String[]) m_cache.get(root);
- if ((result != null) && (result.length == 0))
+ if (m_cache.containsKey(root))
{
- throw new IOException("Bundle not properly signed");
+ Map result = (Map) m_cache.get(root);
+ if ((result != null) && (result.isEmpty()))
+ {
+ throw new IOException("Bundle not properly signed");
+ }
+ return;
}
- return;
+ }
+ }
+ else
+ {
+ synchronized (m_allCache)
+ {
+ if (m_allCache.containsKey(root))
+ {
+ Map result = (Map) m_allCache.get(root);
+ if ((result != null) && (result.isEmpty()))
+ {
+ throw new IOException("Bundle not properly signed");
+ }
+ return;
+ }
}
}
- String[] result = new String[0];
+ Map result = null;
Exception org = null;
try
{
- result = _getDNChains(root, content);
+ result = _getDNChains(content,
+ signersType == Bundle.SIGNERS_TRUSTED);
}
catch (Exception ex)
{
org = ex;
}
- synchronized (m_cache)
+ if (signersType == Bundle.SIGNERS_TRUSTED)
{
- m_cache.put(root, result);
+ synchronized (m_cache)
+ {
+ m_cache.put(root, result);
+ }
+ }
+ else
+ {
+ synchronized (m_allCache)
+ {
+ m_allCache.put(root, result);
+ }
}
if (org != null)
@@ -134,46 +165,70 @@
}
}
- public String[] getDNChains(String root, IContent bundleRevision)
+ public Map getDNChains(IModule root, IContent bundleRevision,
+ int signersType)
{
- synchronized (m_cache)
+ if (signersType == Bundle.SIGNERS_TRUSTED)
{
- if (m_cache.containsKey(root))
+ synchronized (m_cache)
{
- String[] result = (String[]) m_cache.get(root);
- if ((result != null) && (result.length == 0))
+ if (m_cache.containsKey(root))
{
- return null;
+ Map result = (Map) m_cache.get(root);
+ return (result == null) ? new HashMap() : new HashMap(
+ result);
}
- return result;
+ }
+ }
+ else
+ {
+ synchronized (m_allCache)
+ {
+ if (m_allCache.containsKey(root))
+ {
+ Map result = (Map) m_allCache.get(root);
+ return (result == null) ? new HashMap() : new HashMap(
+ result);
+ }
}
}
- String[] result = new String[0];
-
+ Map result = null;
+
try
{
- result = _getDNChains(root, bundleRevision);
+ result = _getDNChains(bundleRevision,
+ signersType == Bundle.SIGNERS_TRUSTED);
}
catch (Exception ex)
{
// Ignore
}
- synchronized (m_cache)
+ if (signersType == Bundle.SIGNERS_TRUSTED)
{
- m_cache.put(root, result);
+ synchronized (m_cache)
+ {
+ m_cache.put(root, result);
+ }
+ }
+ else
+ {
+ synchronized (m_allCache)
+ {
+ m_allCache.put(root, result);
+ }
}
- return result;
- }
+ return (result == null) ? new HashMap() : new HashMap(result);
+ }
- private String[] _getDNChains(String root, IContent content)
+ private Map _getDNChains(IContent content, boolean check)
throws IOException
{
X509Certificate[] certificates = null;
- certificates = getCertificates(new BundleInputStream(content));
+ certificates = getCertificates(new BundleInputStream(content), check);
if (certificates == null)
{
@@ -182,11 +237,9 @@
List rootChains = new ArrayList();
- getRootChains(certificates, rootChains);
+ getRootChains(certificates, rootChains, check);
- List result = new ArrayList();
-
- SubjectDNParser parser = new SubjectDNParser();
+ Map result = new HashMap();
for (Iterator rootIter = rootChains.iterator(); rootIter.hasNext();)
{
@@ -198,42 +251,18 @@
X509Certificate current = (X509Certificate) iter.next();
- try
- {
- buffer.append(parser
- .parseSubjectDN(current.getTBSCertificate()));
-
- while (iter.hasNext())
- {
- buffer.append(';');
-
- current = (X509Certificate) iter.next();
-
- buffer.append(parser.parseSubjectDN(current
- .getTBSCertificate()));
- }
-
- result.add(buffer.toString());
-
- }
- catch (Exception ex)
- {
- // something went wrong during parsing -
- // it might be that the cert contained an unsupported OID
- // TODO: log this or something
- ex.printStackTrace();
- }
+ result.put(current, chain);
}
if (!result.isEmpty())
{
- return (String[]) result.toArray(new String[result.size()]);
+ return result;
}
throw new IOException();
}
- private X509Certificate[] getCertificates(InputStream input)
+ private X509Certificate[] getCertificates(InputStream input, boolean check)
throws IOException
{
JarInputStream bundle = new JarInputStream(input, true);
@@ -250,9 +279,9 @@
// This is tricky: jdk1.3 doesn't say anything about what is happening
// if a bad sig is detected on an entry - later jdk's do say that they
// will throw a security Exception. The below should cater for both
- // behaviors.
- for (JarEntry entry = bundle.getNextJarEntry(); entry != null; entry =
- bundle.getNextJarEntry())
+ // behaviors.
+ for (JarEntry entry = bundle.getNextJarEntry(); entry != null; entry = bundle
+ .getNextJarEntry())
{
if (entry.isDirectory() || entry.getName().startsWith("META-INF"))
@@ -277,8 +306,8 @@
{
try
{
- Object[] signers =
- (Object[]) m_getCodeSigners.invoke(entry, null);
+ Object[] signers = (Object[]) m_getCodeSigners.invoke(
+ entry, null);
if (signers != null)
{
@@ -286,16 +315,15 @@
for (int i = 0; i < signers.length; i++)
{
- Object path =
- m_getSignerCertPath.invoke(signers[i], null);
+ Object path = m_getSignerCertPath.invoke(
+ signers[i], null);
certChains.addAll((List) m_getCertificates.invoke(
path, null));
}
- certificates =
- (Certificate[]) certChains
- .toArray(new Certificate[certChains.size()]);
+ certificates = (Certificate[]) certChains
+ .toArray(new Certificate[certChains.size()]);
}
}
catch (Exception ex)
@@ -312,7 +340,7 @@
List chains = new ArrayList();
- getRootChains(certificates, chains);
+ getRootChains(certificates, chains, check);
if (certificateChains.isEmpty())
{
@@ -324,13 +352,13 @@
for (Iterator iter2 = certificateChains.iterator(); iter2
.hasNext();)
{
- X509Certificate cert =
- (X509Certificate) ((List) iter2.next()).get(0);
+ X509Certificate cert = (X509Certificate) ((List) iter2
+ .next()).get(0);
boolean found = false;
for (Iterator iter3 = chains.iterator(); iter3.hasNext();)
{
- X509Certificate cert2 =
- (X509Certificate) ((List) iter3.next()).get(0);
+ X509Certificate cert2 = (X509Certificate) ((List) iter3
+ .next()).get(0);
if (cert.getSubjectDN().equals(cert2.getSubjectDN())
&& cert.equals(cert2))
@@ -380,7 +408,8 @@
return false;
}
- private void getRootChains(Certificate[] certificates, List chains)
+ private void getRootChains(Certificate[] certificates, List chains,
+ boolean check)
{
List chain = new ArrayList();
@@ -394,11 +423,14 @@
{
revoked = true;
}
- else if (!revoked)
+ if (!check || !revoked)
{
try
{
- certificate.checkValidity();
+ if (check)
+ {
+ certificate.checkValidity();
+ }
chain.add(certificate);
}
@@ -412,7 +444,7 @@
if (!((X509Certificate) certificates[i + 1]).getSubjectDN().equals(
certificate.getIssuerDN()))
{
- if (!revoked && trusted(certificate))
+ if (!check || (!revoked && trusted(certificate)))
{
chains.add(chain);
}
@@ -427,10 +459,11 @@
}
// The final entry in the certs array is always
// a "root" certificate
- if (!revoked)
+ if (!check || !revoked)
{
chain.add(certificates[certificates.length - 1]);
- if (trusted((X509Certificate) certificates[certificates.length - 1]))
+ if (!check
+ || trusted((X509Certificate) certificates[certificates.length - 1]))
{
chains.add(chain);
}
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/verifier/SignerMatcher.java b/framework.security/src/main/java/org/apache/felix/framework/security/verifier/SignerMatcher.java
deleted file mode 100644
index 98509d6..0000000
--- a/framework.security/src/main/java/org/apache/felix/framework/security/verifier/SignerMatcher.java
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.framework.security.verifier;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-import org.apache.felix.moduleloader.IContent;
-import org.apache.felix.moduleloader.IModule;
-
-public final class SignerMatcher
-{
- private final String m_filter;
- private final String m_root;
- private final IContent m_archive;
- private final BundleDNParser m_parser;
- private final long m_lastModified;
-
- public SignerMatcher(String filter)
- {
- m_filter = filter;
- m_root = null;
- m_archive = null;
- m_parser = null;
- m_lastModified = 0;
- }
-
- public SignerMatcher(String root, long lastModified, IContent archive, BundleDNParser parser)
- {
- m_filter = null;
- m_root = root;
- m_archive = archive;
- m_parser = parser;
- m_lastModified = lastModified;
- }
-
- public boolean equals(Object o)
- {
- if (!(o instanceof SignerMatcher))
- {
- return false;
- }
-
- String pattern = ((SignerMatcher) o).m_filter;
-
- if (pattern == null)
- {
- return true;
- }
-
- if (m_archive == null)
- {
- return pattern.trim().equals("\\*");
- }
-
- String[] dns;
- try
- {
- dns = m_parser.getDNChains(m_root + "-" + m_lastModified, m_archive);
- }
- catch (Exception ex)
- {
- // TODO: log this or something
- ex.printStackTrace();
- return false;
- }
-
- if (dns == null)
- {
- return pattern.trim().equals("\\*");
- }
-
- for (int i = 0;i < dns.length;i++)
- {
- if (match(pattern, dns[i]))
- {
- return true;
- }
- }
-
- return false;
- }
-
- public int hashCode()
- {
- return 42;
- }
-
- // see core spec 2.3
- public static boolean match(String pattern, String dn)
- {
- try
- {
- return ((pattern != null) && (dn != null)) ?
- matchDN(pattern.toCharArray(), 0, dn.toCharArray(), 0) : false;
- }
- catch (Exception ex)
- {
- // TODO: log this or something
- ex.printStackTrace();
- }
-
- return false;
- }
-
- private static boolean matchDN(char[] pattern, int pPos, char[] dn, int dPos)
- {
- pPos = skip(pattern, pPos, ' ');
-
- if (pPos >= pattern.length)
- {
- return true;
- }
-
- int befor = pPos;
-
- if ((pPos < pattern.length -1) && (pattern[pPos] == '\\') && (pattern[pPos + 1] == '*'))
- {
- pPos = pPos + 1;
- }
-
- switch (pattern[pPos++])
- {
- case '*':
- pPos = skip(pattern, pPos, ' ');
- if ((pPos < pattern.length) && (pattern[pPos] == ';'))
- {
- if (matchDN(pattern, ++pPos, dn, dPos))
- {
- return true;
- }
- return matchDN(pattern, pPos, dn, skipEscapedUntil(dn, dPos, ';') + 1);
- }
- if (pPos >= pattern.length)
- {
- return true;
- }
- return matchRDN(pattern, befor, dn, dPos);
- case '-':
- pPos = skip(pattern, pPos, ' ');
- if ((pPos < pattern.length) && (pattern[pPos] == ';'))
- {
- int next = dPos;
- pPos++;
- do
- {
- if (matchDN(pattern, pPos, dn, next))
- {
- return true;
- }
- next = skipEscapedUntil(dn, next, ';') + 1;
- } while (next < dn.length);
-
- return false;
- }
- if (pPos >= pattern.length)
- {
- return true;
- }
- throw new IllegalArgumentException("[" + pPos + "]" + new String(pattern));
- default:
- break;
- }
-
- return matchRDN(pattern, befor, dn, dPos);
- }
-
- private static boolean matchRDN(char[] pattern, int pPos, char[] dn, int dPos)
- {
- pPos = skip(pattern, pPos, ' ');
-
- if (pPos >= pattern.length)
- {
- return true;
- }
-
- if ((pPos < pattern.length -1) && (pattern[pPos] == '\\') && (pattern[pPos + 1] == '*'))
- {
- pPos = pPos + 1;
- }
-
- switch (pattern[pPos++])
- {
- case '*':
- pPos = skip(pattern, pPos, ' ');
- if ((pPos < pattern.length) && (pattern[pPos] == ','))
- {
- pPos++;
- do
- {
- if (matchKV(pattern, pPos, dn, dPos))
- {
- return true;
- }
-
- int comma = skipEscapedUntil(dn, dPos, ',');
- int colon = skipEscapedUntil(dn, dPos, ';');
-
- dPos = (comma > colon) ? colon : comma;
- } while ((dPos < dn.length) && (dn[dPos++] == ','));
- return false;
- }
- throw new IllegalArgumentException("[" + pPos + "]" + new String(pattern));
- default:
- break;
- }
-
- return matchKV(pattern, pPos - 1, dn, dPos);
- }
-
- private static boolean matchKV(char[] pattern, int pPos, char[] dn, int dPos)
- {
- pPos = skip(pattern, pPos, ' ');
-
- if (pPos >= pattern.length)
- {
- return false;
- }
-
- int equals = skipEscapedUntil(pattern, pPos, '=');
- int comma = skipEscapedUntil(pattern, pPos, ',');
- int colon = skipEscapedUntil(pattern, pPos, ';');
- if (((colon < pattern.length) && (colon < equals)) ||
- ((comma < pattern.length) && (comma < equals)) ||
- (equals >= pattern.length))
- {
- return false;
- }
-
- String key = (String) KEY2OIDSTRING.get(
- new String(pattern, pPos, equals - pPos).toLowerCase(Locale.US).trim());
-
- if (key == null)
- {
- throw new IllegalArgumentException("Bad key [" +
- new String(pattern, pPos, equals - pPos) + "] in [" +
- new String(pattern) + "]");
- }
-
- pPos = equals + 1;
- int keylength = key.length();
- for (int i = 0;i < keylength;i++)
- {
- if ((dPos >= dn.length) || (key.charAt(i) != dn[dPos++]))
- {
- return false;
- }
- }
-
- if ((dPos >= dn.length) || (dn[dPos++] != '='))
- {
- return false;
- }
-
- pPos = skip(pattern, pPos, ' ');
- if ((pPos < pattern.length -1) && (pattern[pPos] == '\\') && (pattern[pPos + 1] == '*'))
- {
- pPos = skip(pattern, pPos + 2, ' ');
- if (pPos >= pattern.length)
- {
- return true;
- }
- comma = skipEscapedUntil(dn, dPos, ',');
- colon = skipEscapedUntil(dn, dPos, ';');
- if ((pattern[pPos] == ',') && (colon > comma))
- {
- return matchKV(pattern, ++pPos, dn, comma + 1);
- }
-
- if (pattern[pPos] == ';' )
- {
- return matchDN(pattern, ++pPos, dn, colon + 1);
- }
-
- return false;
- }
- boolean escaped = false;
- while ((pPos < pattern.length) && (dPos < dn.length))
- {
- switch (Character.toLowerCase(pattern[pPos++]))
- {
- case ' ':
- if ((pattern[pPos - 2] != ' ') && ((dn[dPos++] != ' ') &&
- (dn[--dPos] != ';') && (dn[dPos] != ',')))
- {
- return false;
- }
- break;
- case '\\':
- escaped = !escaped;
- break;
-
- case '(':
- case ')':
- if (escaped)
- {
- if (dn[dPos++] != pattern[pPos - 1])
- {
- return false;
- }
- escaped = false;
- break;
- }
- return false;
- case ';':
- if (!escaped)
- {
- if ((dPos < dn.length) && ((dn[dPos] == ',') || (dn[dPos] == ';')))
- {
- return matchDN(pattern, pPos, dn, skipEscapedUntil(dn, dPos, ';') + 1);
- }
- return false;
- }
- case ',':
- if (!escaped)
- {
- if ((dPos < dn.length) && (dn[dPos] == ','))
- {
- return matchKV(pattern, pPos, dn, dPos + 1);
- }
- return false;
- }
- default:
- if (escaped)
- {
- if (dn[dPos++] != '\\')
- {
- return false;
- }
- escaped = false;
- }
- if (dn[dPos++] != Character.toLowerCase(pattern[pPos - 1]))
- {
- return false;
- }
- break;
- }
- }
-
- pPos = skip(pattern, pPos, ' ');
- if (pPos >= pattern.length)
- {
- if ((dPos >= dn.length) || (dn[dPos] == ',') || (dn[dPos] == ';'))
- {
- return true;
- }
- }
- else
- {
- switch (pattern[pPos++])
- {
- case ',':
- return matchKV(pattern, pPos, dn, dPos);
- case ';':
- return matchDN(pattern, pPos, dn, dPos);
- default:
- break;
- }
- }
-
- return false;
- }
-
- private static final Map KEY2OIDSTRING = new HashMap();
-
- static {
- KEY2OIDSTRING.put("2.5.4.3", "cn");
- KEY2OIDSTRING.put("cn", "cn");
- KEY2OIDSTRING.put("commonname", "cn");
- KEY2OIDSTRING.put("2.5.4.4", "sn");
- KEY2OIDSTRING.put("sn", "sn");
- KEY2OIDSTRING.put("surname", "sn");
- KEY2OIDSTRING.put("2.5.4.6", "c");
- KEY2OIDSTRING.put("c", "c");
- KEY2OIDSTRING.put("countryname", "c");
- KEY2OIDSTRING.put("2.5.4.7", "l");
- KEY2OIDSTRING.put("l", "l");
- KEY2OIDSTRING.put("localityname", "l");
- KEY2OIDSTRING.put("2.5.4.8", "st");
- KEY2OIDSTRING.put("st", "st");
- KEY2OIDSTRING.put("stateorprovincename", "st");
- KEY2OIDSTRING.put("2.5.4.10", "o");
- KEY2OIDSTRING.put("o", "o");
- KEY2OIDSTRING.put("organizationname", "o");
- KEY2OIDSTRING.put("2.5.4.11", "ou");
- KEY2OIDSTRING.put("ou", "ou");
- KEY2OIDSTRING.put("organizationalunitname", "ou");
- KEY2OIDSTRING.put("2.5.4.12", "title");
- KEY2OIDSTRING.put("t", "title");
- KEY2OIDSTRING.put("title", "title");
- KEY2OIDSTRING.put("2.5.4.42", "givenname");
- KEY2OIDSTRING.put("givenname", "givenname");
- KEY2OIDSTRING.put("2.5.4.43", "initials");
- KEY2OIDSTRING.put("initials", "initials");
- KEY2OIDSTRING.put("2.5.4.44", "generationqualifier");
- KEY2OIDSTRING.put("generationqualifier", "generationqualifier");
- KEY2OIDSTRING.put("2.5.4.46", "dnqualifier");
- KEY2OIDSTRING.put("dnqualifier", "dnqualifier");
- KEY2OIDSTRING.put("2.5.4.9", "street");
- KEY2OIDSTRING.put("street", "street");
- KEY2OIDSTRING.put("streetaddress", "street");
- KEY2OIDSTRING.put("0.9.2342.19200300.100.1.25", "dc");
- KEY2OIDSTRING.put("dc", "dc");
- KEY2OIDSTRING.put("domaincomponent", "dc");
- KEY2OIDSTRING.put("0.9.2342.19200300.100.1.1", "uid");
- KEY2OIDSTRING.put("uid", "uid");
- KEY2OIDSTRING.put("userid", "uid");
- KEY2OIDSTRING.put("1.2.840.113549.1.9.1", "emailaddress");
- KEY2OIDSTRING.put("emailaddress", "emailaddress");
- KEY2OIDSTRING.put("2.5.4.5", "serialnumber");
- KEY2OIDSTRING.put("serialnumber", "serialnumber");
- }
-
- private static int skipEscapedUntil(char[] string, int pos, char value)
- {
- boolean escaped = false;
-
- while (pos < string.length)
- {
- switch (string[pos++])
- {
- case '\\':
- escaped = true;
- break;
- default:
- if (!escaped)
- {
- if (string[pos - 1] == value)
- {
- return pos - 1;
- }
- }
- escaped = false;
- break;
- }
- }
-
- return pos;
- }
-
- private static int skip(char[] string, int pos, char value)
- {
- while (pos < string.length)
- {
- if (string[pos] != value)
- {
- break;
- }
- pos++;
- }
-
- return pos;
- }
-}
diff --git a/framework.security/src/main/java/org/apache/felix/framework/security/verifier/SubjectDNParser.java b/framework.security/src/main/java/org/apache/felix/framework/security/verifier/SubjectDNParser.java
deleted file mode 100644
index 1c34021..0000000
--- a/framework.security/src/main/java/org/apache/felix/framework/security/verifier/SubjectDNParser.java
+++ /dev/null
@@ -1,451 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.framework.security.verifier;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-public final class SubjectDNParser
-{
- private static final Map OID2NAME = new HashMap();
-
- static
- {
- // see core-spec 2.3.5
- OID2NAME.put("2.5.4.3", "cn");
- OID2NAME.put("2.5.4.4", "sn");
- OID2NAME.put("2.5.4.6", "c");
- OID2NAME.put("2.5.4.7", "l");
- OID2NAME.put("2.5.4.8", "st");
- OID2NAME.put("2.5.4.10", "o");
- OID2NAME.put("2.5.4.11", "ou");
- OID2NAME.put("2.5.4.12", "title");
- OID2NAME.put("2.5.4.42", "givenname");
- OID2NAME.put("2.5.4.43", "initials");
- OID2NAME.put("2.5.4.44", "generationqualifier");
- OID2NAME.put("2.5.4.46", "dnqualifier");
- OID2NAME.put("2.5.4.9", "street");
- OID2NAME.put("0.9.2342.19200300.100.1.25", "dc");
- OID2NAME.put("0.9.2342.19200300.100.1.1", "uid");
- OID2NAME.put("1.2.840.113549.1.9.1", "emailaddress");
- OID2NAME.put("2.5.4.5", "serialnumber");
- // p.s.: it sucks that the spec doesn't list some of the oids used
- // p.p.s: it sucks that the spec doesn't list the short form for all names
- // In summary, there is a certain amount of guess-work involved but I'm
- // fairly certain I've got it right.
- }
-
- private byte[] m_buffer;
- private int m_offset = 0;
- private int m_tagOffset = 0;
- private int m_tag = -1;
- private int m_length = -1;
- private int m_contentOffset = -1;
-
- /*
- * This is deep magiK, bare with me. The problem is that we don't get
- * access to the original subject dn in a certificate without resorting to
- * sun.* classes or running on something > OSGi-minimum/jdk1.3. Furthermore,
- * we need access to it because there is no other way to escape it properly.
- * Note, this is due to missing of a public X500Name in OSGI-minimum/jdk1.3
- * a.k.a foundation.
- *
- * The solution is to get the DER encoded TBS certificate bytes via the
- * available java methods and parse-out the subject dn in canonical form by
- * hand. This is possible without deploying a full-blown BER encoder/decoder
- * due to java already having done all the cumbersome verification and
- * normalization work.
- *
- * The following skips through the TBS certificate bytes until it reaches and
- * subsequently parses the subject dn. If the below makes immediate sense to
- * you - you either are a X509/X501/DER expert or quite possibly mad. In any
- * case, please seek medical care immediately.
- */
- public String parseSubjectDN(byte[] tbsBuffer) throws Exception
- {
- // init
- m_buffer = tbsBuffer;
- m_offset = 0;
-
- // TBSCertificate ::= SEQUENCE {
- // version [0] EXPLICIT Version DEFAULT v1,
- // serialNumber CertificateSerialNumber,
- // signature AlgorithmIdentifier,
- // issuer Name,
- // validity Validity,
- // subject Name,
- //
- // WE CAN STOP!
- //
- // subjectPublicKeyInfo SubjectPublicKeyInfo,
- // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
- // -- If present, version must be v2 or v3
- // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
- // -- If present, version must be v2 or v3
- // extensions [3] EXPLICIT Extensions OPTIONAL
- // -- If present, version must be v3
- // }
- try
- {
- next();
- next();
- // if a version is present skip it
- if (m_tag == 0)
- {
- next();
- m_offset += m_length;
- }
- m_offset += m_length;
- // skip the serialNumber
- next();
- next();
- m_offset += m_length;
- // skip the signature
- next();
- m_offset += m_length;
- // skip the issuer
- // The issuer is a sequence of sets of issuer dns like the subject later on -
- // we just skip it.
- next();
- int endOffset = m_offset + m_length;
-
- int seqTagOffset = m_tagOffset;
-
- // skip the sequence
- while (endOffset > m_offset)
- {
- next();
-
- int endOffset2 = m_offset + m_length;
-
- int seqTagOffset2 = m_tagOffset;
-
- // skip each set
- while (endOffset2 > m_offset)
- {
- next();
- next();
- m_offset += m_length;
- next();
- m_offset += m_length;
- }
-
- m_tagOffset = seqTagOffset2;
- }
-
- m_tagOffset = seqTagOffset;
- // skip the validity which contains two dates to be skiped
- next();
- next();
- m_offset += m_length;
- next();
- m_offset += m_length;
- next();
- // Now extract the subject dns and add them to attributes
- List attributes = new ArrayList();
-
- endOffset = m_offset + m_length;
-
- seqTagOffset = m_tagOffset;
-
- // for each set of rdns
- while (endOffset > m_offset)
- {
- next();
- int endOffset2 = m_offset + m_length;
-
- // store tag offset
- int seqTagOffset2 = m_tagOffset;
-
- List rdn = new ArrayList();
-
- // for each rdn in the set
- while (endOffset2 > m_offset)
- {
- next();
- next();
- m_offset += m_length;
- // parse the oid of the rdn
- int oidElement = 1;
- for (int i = 0; i < m_length; i++, ++oidElement)
- {
- while ((m_buffer[m_contentOffset + i] & 0x80) == 0x80)
- {
- i++;
- }
- }
- int[] oid = new int[oidElement];
- for (int id = 1, i = 0; id < oid.length; id++, i++)
- {
- int octet = m_buffer[m_contentOffset + i];
- oidElement = octet & 0x7F;
- while ((octet & 0x80) != 0)
- {
- i++;
- octet = m_buffer[m_contentOffset + i];
- oidElement = oidElement << 7 | (octet & 0x7f);
- }
- oid[id] = oidElement;
- }
- // The first OID is special
- if (oid[1] > 79)
- {
- oid[0] = 2;
- oid[1] = oid[1] - 80;
- }
- else
- {
- oid[0] = oid[1] / 40;
- oid[1] = oid[1] % 40;
- }
- // Now parse the value of the rdn
- next();
- String str = null;
- int tagTmp = m_tag;
- m_offset += m_length;
- switch(tagTmp)
- {
- case 30: // BMPSTRING
- case 22: // IA5STRING
- case 27: // GENERALSTRING
- case 19: // PRINTABLESTRING
- case 20: // TELETEXSTRING && T61STRING
- case 28: // UNIVERSALSTRING
- str = new String(m_buffer, m_contentOffset,
- m_length);
- break;
- case 12: // UTF8_STRING
- str = new String(m_buffer, m_contentOffset,
- m_length, "UTF-8");
- break;
- default: // OCTET
- byte[] encoded = new byte[m_offset - m_tagOffset];
- System.arraycopy(m_buffer, m_tagOffset, encoded,
- 0, encoded.length);
- // Note, I'm not sure this is allowed by the spec
- // i.e., whether OCTET subjects are allowed at all
- // but it shouldn't harm doing it anyways (we just
- // convert it into a hex string prefixed with \#).
- str = toHexString(encoded);
- break;
- }
-
- rdn.add(new Object[]{mapOID(oid), makeCanonical(str)});
- }
-
- attributes.add(rdn);
- m_tagOffset = seqTagOffset2;
- }
-
- m_tagOffset = seqTagOffset;
-
- StringBuffer result = new StringBuffer();
-
- for (int i = attributes.size() - 1; i >= 0; i--)
- {
- List rdn = (List) attributes.get(i);
- Collections.sort(rdn, new Comparator()
- {
- public int compare(Object obj1, Object obj2)
- {
- return ((String) ((Object[]) obj1)[0]).compareTo(
- ((String) ((Object[])obj2)[0]));
- }
- });
-
- for (Iterator iter = rdn.iterator();iter.hasNext();)
- {
- Object[] att = (Object[]) iter.next();
- result.append((String) att[0]);
- result.append('=');
- result.append((String) att[1]);
-
- if (iter.hasNext())
- {
- // multi-valued RDN
- result.append('+');
- }
- }
-
- if (i != 0)
- {
- result.append(',');
- }
- }
-
-
- // the spec says:
- // return result.toString().toUpperCase(Locale.US).toLowerCase(Locale.US);
- // which is needed because toLowerCase can be ambiguous in unicode when
- // used on mixed case while toUpperCase not hence, this way its ok.
- return result.toString().toUpperCase(Locale.US).toLowerCase(Locale.US);
- }
- finally
- {
- m_buffer = null;
- }
- }
-
- // Determine the type of the current sequence (tbs_tab), and the length and
- // offset of it (tbs_length and tbs_tagOffset) plus increment the global
- // offset (tbs_offset) accordingly. Note, we don't need to check for
- // the indefinite length because this is supposed to be DER not BER (and
- // we implicitly assume that java only gives us valid DER).
- private void next()
- {
- m_tagOffset = m_offset;
- m_tag = m_buffer[m_offset++] & 0xFF;
- m_length = m_buffer[m_offset++] & 0xFF;
- // There are two kinds of length forms - make sure we use the right one
- if ((m_length & 0x80) != 0)
- {
- // its the long kind
- int numOctets = m_length & 0x7F;
- // hence, convert it
- m_length = m_buffer[m_offset++] & 0xFF;
- for (int i = 1; i < numOctets; i++)
- {
- int ch = m_buffer[m_offset++] & 0xFF;
- m_length = (m_length << 8) + ch;
- }
- }
- m_contentOffset = m_offset;
- }
-
- private String makeCanonical(String value)
- {
- int len = value.length();
-
- if (len == 0)
- {
- return value;
- }
-
- StringBuffer result = new StringBuffer(len);
-
- int i = 0;
- if (value.charAt(0) == '#')
- {
- result.append('\\');
- result.append('#');
- i++;
- }
- for (;i < len; i++)
- {
- char c = value.charAt(i);
-
- switch (c)
- {
- case ' ':
- int pos = result.length();
- // remove leading spaces and
- // remove all spaces except one in any sequence of spaces
- if ((pos == 0) || (result.charAt(pos - 1) == ' '))
- {
- break;
- }
- result.append(' ');
- break;
- case '"':
- case '\\':
- case ',':
- case '+':
- case '<':
- case '>':
- case ';':
- result.append('\\');
- default:
- result.append(c);
- }
- }
-
- // count down until first none space to remove trailing spaces
- i = result.length() - 1;
- while ((i > -1) && (result.charAt(i) == ' '))
- {
- i--;
- }
-
- result.setLength(i + 1);
-
- return result.toString();
- }
-
- private String toHexString(byte[] encoded)
- {
- StringBuffer result = new StringBuffer();
-
- result.append('#');
-
- for (int i = 0; i < encoded.length; i++)
- {
- int c = (encoded[i] >> 4) & 0x0F;
- if (c < 10)
- {
- result.append((char) (c + 48));
- }
- else
- {
- result.append((char) (c + 87));
- }
-
- c = encoded[i] & 0x0F;
-
- if (c < 10)
- {
- result.append((char) (c + 48));
- }
- else
- {
- result.append((char) (c + 87));
- }
- }
-
- return result.toString();
- }
-
- // This just creates a string of the oid and looks for its name in the
- // known names map OID2NAME. There might be faster implementations :-)
- private String mapOID(int[] oid)
- {
- StringBuffer oidString = new StringBuffer();
-
- oidString.append(oid[0]);
- for (int i = 1;i < oid.length;i++)
- {
- oidString.append('.');
- oidString.append(oid[i]);
- }
-
- String result = (String) OID2NAME.get(oidString.toString());
-
- if (result == null)
- {
- throw new IllegalArgumentException("Unknown oid: " + oidString.toString());
- }
-
- return result;
- }
-}
\ No newline at end of file
diff --git a/framework.security/src/main/java/org/osgi/service/condpermadmin/BundleLocationCondition.java b/framework.security/src/main/java/org/osgi/service/condpermadmin/BundleLocationCondition.java
deleted file mode 100644
index 3f66514..0000000
--- a/framework.security/src/main/java/org/osgi/service/condpermadmin/BundleLocationCondition.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * $Header: /cvshome/build/org.osgi.service.condpermadmin/src/org/osgi/service/condpermadmin/BundleLocationCondition.java,v 1.18 2006/06/16 16:31:37 hargrave Exp $
- *
- * Copyright (c) OSGi Alliance (2005, 2006). All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.osgi.service.condpermadmin;
-
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.Hashtable;
-
-import org.osgi.framework.*;
-
-/**
- * Condition to test if the location of a bundle matches a pattern. Pattern
- * matching is done according to the filter string matching rules.
- *
- * @version $Revision: 1.18 $
- */
-public class BundleLocationCondition
-{
- private static final String CONDITION_TYPE =
- "org.osgi.service.condpermadmin.BundleLocationCondition";
-
- /**
- * Constructs a condition that tries to match the passed Bundle's location
- * to the location pattern.
- *
- * @param bundle
- * The Bundle being evaluated.
- * @param info
- * The ConditionInfo to construct the condition for. The args
- * of the ConditionInfo must be a single String which
- * specifies the location pattern to match against the Bundle
- * location. Matching is done according to the filter string
- * matching rules. Any '*' characters in the location
- * argument are used as wildcards when matching bundle
- * locations unless they are escaped with a '\' character.
- * @return Condition object for the requested condition.
- */
- static public Condition getCondition(final Bundle bundle, ConditionInfo info)
- {
- if (!CONDITION_TYPE.equals(info.getType()))
- throw new IllegalArgumentException(
- "ConditionInfo must be of type \"" + CONDITION_TYPE + "\"");
- String[] args = info.getArgs();
- if (args.length != 1)
- throw new IllegalArgumentException("Illegal number of args: "
- + args.length);
- String bundleLocation =
- (String) AccessController.doPrivileged(new PrivilegedAction()
- {
- public Object run()
- {
- return bundle.getLocation();
- }
- });
- Filter filter = null;
- try
- {
- filter =
- FrameworkUtil.createFilter("(location=" + escapeLocation(args[0]) + ")");
- }
- catch (InvalidSyntaxException e)
- {
- // this should never happen, but just incase
- throw new RuntimeException("Invalid filter: " + e.getFilter());
- }
- Hashtable matchProps = new Hashtable(2);
- matchProps.put("location", bundleLocation);
- return filter.match(matchProps) ? Condition.TRUE : Condition.FALSE;
- }
-
- private BundleLocationCondition()
- {
- // private constructor to prevent objects of this type
- }
-
- /**
- * Escape the value string such that '(', ')' and '\' are escaped. The '\'
- * char is only escaped if it is not followed by a '*'.
- *
- * @param value
- * unescaped value string.
- * @return escaped value string.
- */
- private static String escapeLocation(String value)
- {
- boolean escaped = false;
- int inlen = value.length();
- int outlen = inlen << 1; /* inlen * 2 */
-
- char[] output = new char[outlen];
- value.getChars(0, inlen, output, inlen);
-
- int cursor = 0;
- for (int i = inlen; i < outlen; i++)
- {
- char c = output[i];
- switch (c)
- {
- case '\\':
- if (i + 1 < outlen && output[i + 1] == '*')
- break;
- case '(':
- case ')':
- output[cursor] = '\\';
- cursor++;
- escaped = true;
- break;
- }
-
- output[cursor] = c;
- cursor++;
- }
-
- return escaped ? new String(output, 0, cursor) : value;
- }
-}
diff --git a/framework.security/src/main/java/org/osgi/service/condpermadmin/BundleSignerCondition.java b/framework.security/src/main/java/org/osgi/service/condpermadmin/BundleSignerCondition.java
deleted file mode 100644
index 91f2854..0000000
--- a/framework.security/src/main/java/org/osgi/service/condpermadmin/BundleSignerCondition.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * $Header: /cvshome/build/org.osgi.service.condpermadmin/src/org/osgi/service/condpermadmin/BundleSignerCondition.java,v 1.10 2006/06/16 16:31:37 hargrave Exp $
- *
- * Copyright (c) OSGi Alliance (2005, 2006). All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.osgi.service.condpermadmin;
-
-import java.lang.reflect.Method;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.Filter;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-
-/**
- * Condition to test if the signer of a bundle matches a pattern. Since the
- * bundle's signer can only change when the bundle is updated, this condition is
- * immutable.
- * <p>
- * The condition expressed using a single String that specifies a Distinguished
- * Name (DN) chain to match bundle signers against. DN's are encoded using IETF
- * RFC 2253. Usually signers use certificates that are issued by certificate
- * authorities, which also have a corresponding DN and certificate. The
- * certificate authorities can form a chain of trust where the last DN and
- * certificate is known by the framework. The signer of a bundle is expressed as
- * signers DN followed by the DN of its issuer followed by the DN of the next
- * issuer until the DN of the root certificate authority. Each DN is separated
- * by a semicolon.
- * <p>
- * A bundle can satisfy this condition if one of its signers has a DN chain that
- * matches the DN chain used to construct this condition. Wildcards (`*') can be
- * used to allow greater flexibility in specifying the DN chains. Wildcards can
- * be used in place of DNs, RDNs, or the value in an RDN. If a wildcard is used
- * for a value of an RDN, the value must be exactly "*" and will match any value
- * for the corresponding type in that RDN. If a wildcard is used for a RDN, it
- * must be the first RDN and will match any number of RDNs (including zero
- * RDNs).
- *
- * @version $Revision: 1.10 $
- */
-/*
- * TODO: In our case the above is not correct. We don't make this an immutable
- * condition because the spec is somewhat ambiguous in regard to when the
- * signature change. This probably has to be clarified and then revisited later.
- */
-public class BundleSignerCondition
-{
- /*
- * NOTE: A framework implementor may also choose to replace this class in
- * their distribution with a class that directly interfaces with the
- * framework implementation. This replacement class MUST NOT alter the
- * public/protected signature of this class.
- */
-
- private static final String CONDITION_TYPE =
- "org.osgi.service.condpermadmin.BundleSignerCondition";
-
- /**
- * Constructs a Condition that tries to match the passed Bundle's location
- * to the location pattern.
- *
- * @param bundle
- * The Bundle being evaluated.
- * @param info
- * The ConditionInfo to construct the condition for. The args
- * of the ConditionInfo specify a single String specifying
- * the chain of distinguished names pattern to match against
- * the signer of the Bundle.
- * @return A Condition which checks the signers of the specified bundle.
- */
- static public Condition getCondition(Bundle bundle, ConditionInfo info)
- {
- if (!CONDITION_TYPE.equals(info.getType()))
- throw new IllegalArgumentException(
- "ConditionInfo must be of type \"" + CONDITION_TYPE + "\"");
- final String[] args = info.getArgs();
- if (args.length != 1)
- throw new IllegalArgumentException("Illegal number of args: "
- + args.length);
-
- return new ConditionImpl(bundle, "(signer=" + escapeFilter(args[0])
- + ")");
-
- }
-
- private static String escapeFilter(String string)
- {
- boolean escaped = false;
- int inlen = string.length();
- int outlen = inlen << 1; /* inlen * 2 */
-
- char[] output = new char[outlen];
- string.getChars(0, inlen, output, inlen);
-
- int cursor = 0;
- for (int i = inlen; i < outlen; i++)
- {
- char c = output[i];
- switch (c)
- {
- case '\\':
- case '(':
- case ')':
- case '*':
- output[cursor] = '\\';
- cursor++;
- escaped = true;
- break;
- }
-
- output[cursor] = c;
- cursor++;
- }
-
- return escaped ? new String(output, 0, cursor) : string;
- }
-
- private BundleSignerCondition()
- {
- // private constructor to prevent objects of this type
- }
-}
-
-final class ConditionImpl implements Condition, PrivilegedExceptionAction
-{
- private static final Method m_getSignerMatcher;
-
- static
- {
- m_getSignerMatcher =
- (Method) AccessController.doPrivileged(new PrivilegedAction()
- {
- public Object run()
- {
- Method getSignerMatcher = null;
- try
- {
- getSignerMatcher =
- Class.forName(
- "org.apache.felix.framework.BundleImpl")
- .getDeclaredMethod("getSignerMatcher", null);
- getSignerMatcher.setAccessible(true);
- }
- catch (Exception ex)
- {
- ex.printStackTrace();
- getSignerMatcher = null;
- }
- return getSignerMatcher;
- }
- });
- }
-
- private final Bundle m_bundle;
- private final Filter m_filter;
- private final Dictionary m_dict;
-
- ConditionImpl(Bundle bundle, String filter)
- {
- m_bundle = bundle;
- try
- {
- m_filter = FrameworkUtil.createFilter(filter);
- }
- catch (InvalidSyntaxException e)
- {
- throw new IllegalArgumentException(e.getMessage());
- }
- try
- {
- Object signerMatcher = AccessController.doPrivileged(this);
- m_dict = new Hashtable();
- m_dict.put("signer", signerMatcher);
- }
- catch (PrivilegedActionException e)
- {
- if (e.getException() instanceof RuntimeException)
- {
- throw (RuntimeException) e.getException();
- }
-
- throw new RuntimeException(e.getException().getMessage());
- }
- }
-
- public boolean isMutable()
- {
- return true;
- }
-
- public boolean isPostponed()
- {
- return false;
- }
-
- public Object run() throws Exception
- {
- return m_getSignerMatcher.invoke(m_bundle, null);
- }
-
- public boolean isSatisfied()
- {
- return m_filter.match(m_dict);
- }
-
- public boolean isSatisfied(Condition[] conditions, Dictionary context)
- {
- return false;
- }
-}
\ No newline at end of file