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