blob: a57e318daa61915fba17cb2da677d9ee1539ab59 [file] [log] [blame]
/*
* $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.apache.felix.framework.FilterImpl;
import org.osgi.framework.Bundle;
import org.osgi.framework.Filter;
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 = new FilterImpl(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;
}
}