You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2006/08/24 14:19:33 UTC

svn commit: r434380 - /incubator/felix/trunk/framework/src/main/java/org/osgi/framework/AdminPermission.java

Author: pauls
Date: Thu Aug 24 05:19:31 2006
New Revision: 434380

URL: http://svn.apache.org/viewvc?rev=434380&view=rev
Log:
Implement signer subject dn matching for digitally signed bundles (FELIX-22).

Modified:
    incubator/felix/trunk/framework/src/main/java/org/osgi/framework/AdminPermission.java

Modified: incubator/felix/trunk/framework/src/main/java/org/osgi/framework/AdminPermission.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/framework/src/main/java/org/osgi/framework/AdminPermission.java?rev=434380&r1=434379&r2=434380&view=diff
==============================================================================
--- incubator/felix/trunk/framework/src/main/java/org/osgi/framework/AdminPermission.java (original)
+++ incubator/felix/trunk/framework/src/main/java/org/osgi/framework/AdminPermission.java Thu Aug 24 05:19:31 2006
@@ -16,6 +16,7 @@
  */
 package org.osgi.framework;
 
+import java.lang.reflect.Method;
 import java.security.*;
 import java.util.*;
 
@@ -50,7 +51,7 @@
     private static final int RESOLVE_MASK = 64;
     private static final int RESOURCE_MASK = 128;
     private static final int STARTLEVEL_MASK = 256;
-    private static final int ALL_MASK = 
+    private static final int ALL_MASK =
         CLASS_MASK | EXECUTE_MASK | EXTENSIONLIFECYCLE_MASK |
         LIFECYCLE_MASK | LISTENER_MASK | METADATA_MASK |
         RESOLVE_MASK | RESOURCE_MASK | STARTLEVEL_MASK;
@@ -138,14 +139,14 @@
 
         AdminPermission admin = (AdminPermission) p;
 
-        // Make sure that the permission was create with a bundle or a "*". 
+        // Make sure that the permission was create with a bundle or a "*".
         // Otherwise, throw an Exception - as per spec.
         if ((admin.m_bundle == null) && !(admin.getName().equals("(id=*)")))
         {
             throw new RuntimeException(
                 "The specified permission was not constructed with a bundle or *!");
         }
-        
+
         // Make sure the action mask is a subset.
         if ((m_actionMask & admin.m_actionMask) != admin.m_actionMask)
         {
@@ -159,14 +160,14 @@
         {
             return getName().equals("(id=*)");
         }
-        
+
         // Next, if this object was create with a "*" we can return true
         // (This way we avoid creating and matching a filter).
         if (getName().equals("(id=*)"))
         {
             return true;
         }
-        
+
         // Otherwise, see if this permission's filter matches the
         // dictionary of the passed in permission.
         if (m_filterImpl == null)
@@ -196,11 +197,17 @@
             // Add bundle properties to dictionary.
             m_bundleDict = new Hashtable();
             m_bundleDict.put("id", new Long(m_bundle.getBundleId()));
-            m_bundleDict.put("name", m_bundle.getSymbolicName());
+
+            String symbolicName = m_bundle.getSymbolicName();
+            if (symbolicName != null)
+            {
+                m_bundleDict.put("name", symbolicName);
+            }
             // Add location in privileged block since it performs a security check.
             if (System.getSecurityManager() != null)
             {
-                AccessController.doPrivileged(new PrivilegedAction() {
+                AccessController.doPrivileged(new PrivilegedAction()
+                {
                     public Object run()
                     {
                         m_bundleDict.put("location", m_bundle.getLocation());
@@ -212,7 +219,8 @@
             {
                 m_bundleDict.put("location", m_bundle.getLocation());
             }
-            // TODO: SECURITY - Add bundle signer properties to dictionary.
+
+            m_bundleDict.put("signer", new Signer(m_bundle));
         }
         return m_bundleDict;
     }
@@ -279,55 +287,55 @@
     private static String createActionString(int mask)
     {
         StringBuffer sb = new StringBuffer();
-        
+
         if ((mask & CLASS_MASK) > 0)
         {
             sb.append(CLASS);
             sb.append(",");
         }
-        
+
         if ((mask & EXECUTE_MASK) > 0)
         {
             sb.append(EXECUTE);
             sb.append(",");
         }
-        
+
         if ((mask & EXTENSIONLIFECYCLE_MASK) > 0)
         {
             sb.append(EXTENSIONLIFECYCLE);
             sb.append(",");
         }
-        
+
         if ((mask & LIFECYCLE_MASK) > 0)
         {
             sb.append(LIFECYCLE);
             sb.append(",");
         }
-        
+
         if ((mask & LISTENER_MASK) > 0)
         {
             sb.append(LISTENER);
             sb.append(",");
         }
-        
+
         if ((mask & METADATA_MASK) > 0)
         {
             sb.append(METADATA);
             sb.append(",");
         }
-        
+
         if ((mask & RESOLVE_MASK) > 0)
         {
             sb.append(RESOLVE);
             sb.append(",");
         }
-        
+
         if ((mask & RESOURCE_MASK) > 0)
         {
             sb.append(RESOURCE);
             sb.append(",");
         }
-        
+
         if ((mask & STARTLEVEL_MASK) > 0)
         {
             sb.append(STARTLEVEL);
@@ -406,6 +414,459 @@
         public Enumeration elements()
         {
             return Collections.enumeration(m_map.values());
+        }
+    }
+
+    public static final class Signer implements PrivilegedAction
+    {
+        private Bundle m_bundleImpl = null;
+        private String m_filter = null;
+        private Method m_getSubjectDNs = null;
+        private boolean m_init = false;
+
+        public Signer(String filter)
+        {
+            m_filter = filter;
+        }
+
+        Signer(Bundle bundle)
+        {
+            m_bundleImpl = bundle;
+        }
+
+        public boolean equals(Object o)
+        {
+            if (!(o instanceof Signer))
+            {
+                return false;
+            }
+
+            String pattern = ((Signer) o).m_filter;
+
+            if (pattern == null)
+            {
+                return true;
+            }
+
+            String[] dns = getSubjectDNs();
+
+            if (dns == null)
+            {
+                return pattern.trim().equals("\\*");
+            }
+
+            for (int i = 0;i < dns.length;i++)
+            {
+                if (match(pattern, dns[i]))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        private String[] getSubjectDNs()
+        {
+            if (System.getSecurityManager() != null)
+            {
+                return (String[]) AccessController.doPrivileged(this);
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        public Object run()
+        {
+            try
+            {
+                if (!m_init)
+                {
+                    m_getSubjectDNs =
+                        m_bundleImpl.getClass().getDeclaredMethod("getSubjectDNs", null);
+
+                    m_init = true;
+                }
+
+                m_getSubjectDNs.setAccessible(true);
+
+                return m_getSubjectDNs.invoke(m_bundleImpl, null);
+
+            }
+            catch (Exception ex)
+            {
+                // TODO: log this or something
+                ex.printStackTrace();
+            }
+
+            return null;
+        }
+
+        private 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;
         }
     }
 }