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 2014/10/01 15:49:06 UTC

svn commit: r1628702 - in /felix/trunk/framework/src/main/java/org/apache/felix/framework: BundleProtectionDomain.java util/SecureAction.java

Author: pauls
Date: Wed Oct  1 13:49:05 2014
New Revision: 1628702

URL: http://svn.apache.org/r1628702
Log:
Make the URL of the CodeSource of a BundleProtectionDomain serve up the jar of the bundle to enable proper jce extension checks with the jdk (FELIX-4658).

Modified:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java?rev=1628702&r1=1628701&r2=1628702&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleProtectionDomain.java Wed Oct  1 13:49:05 2014
@@ -18,36 +18,367 @@
  */
 package org.apache.felix.framework;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.lang.ref.WeakReference;
+import java.net.JarURLConnection;
 import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
 import java.security.CodeSource;
 import java.security.Permission;
 import java.security.ProtectionDomain;
 import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import org.apache.felix.framework.cache.Content;
+import org.apache.felix.framework.cache.JarContent;
 
 import org.osgi.framework.wiring.BundleRevision;
 
 public class BundleProtectionDomain extends ProtectionDomain
 {
+    private static final class BundleInputStream extends InputStream
+    {
+        private final Content m_root;
+        private final Enumeration m_content;
+        private final OutputStreamBuffer m_outputBuffer = new OutputStreamBuffer();
+
+        private ByteArrayInputStream m_buffer = null;
+        private JarOutputStream m_output = null;
+
+        private static final String DUMMY_ENTRY = "__DUMMY-ENTRY__/";
+
+        public BundleInputStream(Content root) throws IOException
+        {
+            m_root = root;
+
+            List entries = new ArrayList();
+
+            int count = 0;
+            String manifest = null;
+            for (Enumeration e = m_root.getEntries(); e.hasMoreElements();)
+            {
+                String entry = (String) e.nextElement();
+                if (entry.endsWith("/"))
+                {
+                    // ignore
+                }
+                else if (entry.equalsIgnoreCase("META-INF/MANIFEST.MF"))
+                {
+                    if (manifest == null)
+                    {
+                        manifest = entry;
+                    }
+                }
+                else if (entry.toUpperCase().startsWith("META-INF/")
+                            && entry.indexOf('/', "META-INF/".length()) < 0)
+                {
+                    entries.add(count++, entry);
+                }
+                else
+                {
+                    entries.add(entry);
+                }
+            }
+            entries.add(count++, DUMMY_ENTRY);
+            if (manifest == null)
+            {
+                manifest = "META-INF/MANIFEST.MF";
+            }
+            m_content = Collections.enumeration(entries);
+
+            m_output = new JarOutputStream(m_outputBuffer);
+            readNext(manifest);
+            m_buffer = new ByteArrayInputStream(m_outputBuffer.m_outBuffer
+                .toByteArray());
+
+            m_outputBuffer.m_outBuffer = null;
+        }
+
+        public int read() throws IOException
+        {
+            if ((m_output == null) && (m_buffer == null))
+            {
+                return -1;
+            }
+
+            if (m_buffer != null)
+            {
+                int result = m_buffer.read();
+
+                if (result == -1)
+                {
+                    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_outputBuffer.m_outBuffer = null;
+            }
+            else
+            {
+                m_output.close();
+                m_output = null;
+            }
+
+            return read();
+        }
+
+        private void readNext(String path) throws IOException
+        {
+            m_outputBuffer.m_outBuffer = new ByteArrayOutputStream();
+
+            if (path == DUMMY_ENTRY)
+            {
+                JarEntry entry = new JarEntry(path);
+
+                m_output.putNextEntry(entry);
+            }
+            else
+            {
+                InputStream in = null;
+                try
+                {
+                    in = m_root.getEntryAsStream(path);
+
+                    if (in == null)
+                    {
+                        throw new IOException("Missing entry");
+                    }
+
+                    JarEntry entry = new JarEntry(path);
+
+                    m_output.putNextEntry(entry);
+
+                    byte[] buffer = new byte[4 * 1024];
+
+                    for (int c = in.read(buffer); c != -1; c = in.read(buffer))
+                    {
+                        m_output.write(buffer, 0, c);
+                    }
+                }
+                finally
+                {
+                    if (in != null)
+                    {
+                        try
+                        {
+                            in.close();
+                        }
+                        catch (Exception ex)
+                        {
+                            // Not much we can do
+                        }
+                    }
+                }
+            }
+
+            m_output.closeEntry();
+
+            m_output.flush();
+        }
+    }
+
+    private static final class OutputStreamBuffer extends OutputStream
+    {
+        ByteArrayOutputStream m_outBuffer = null;
+
+        public void write(int b)
+        {
+            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);
+        }
+    }
+
+    private static final class RevisionAsJarURL extends URLStreamHandler
+    {
+        private final WeakReference m_revision;
+
+        private RevisionAsJarURL(BundleRevisionImpl revision)
+        {
+            m_revision = new WeakReference(revision);
+        }
+
+
+        @Override
+        protected URLConnection openConnection(URL u) throws IOException
+        {
+            return new JarURLConnection(u)
+            {
+                @Override
+                public JarFile getJarFile() throws IOException
+                {
+                    BundleRevisionImpl revision = (BundleRevisionImpl) m_revision.get();
+
+                    if (revision != null)
+                    {
+                        Content content = ((BundleRevisionImpl) m_revision.get()).getContent();
+                        if (content instanceof JarContent)
+                        {
+                            return Felix.m_secureAction.openJarFile(((JarContent) content).getFile());
+                        }
+                        else
+                        {
+                            File target = Felix.m_secureAction.createTempFile("jar", null, null);
+                            Felix.m_secureAction.deleteFileOnExit(target);
+                            FileOutputStream output = null;
+                            InputStream input = null;
+                            IOException rethrow = null;
+                            try
+                            {
+                                output = new FileOutputStream(target);
+                                input = new BundleInputStream(revision.getContent());
+                                byte[] buffer = new byte[64 * 1024];
+                                for (int i = input.read(buffer);i != -1; i = input.read(buffer))
+                                {
+                                    output.write(buffer,0, i);
+                                }
+                            }
+                            catch (IOException ex)
+                            {
+                                rethrow = ex;
+                            }
+                            finally
+                            {
+                                if (output != null)
+                                {
+                                    try
+                                    {
+                                        output.close();
+                                    }
+                                    catch (IOException ex)
+                                    {
+                                        if (rethrow == null)
+                                        {
+                                            rethrow = ex;
+                                        }
+                                    }
+                                }
+
+                                if (input != null)
+                                {
+                                    try
+                                    {
+                                        input.close();
+                                    }
+                                    catch (IOException ex)
+                                    {
+                                        if (rethrow == null)
+                                        {
+                                            rethrow = ex;
+                                        }
+                                    }
+                                }
+
+                                if (rethrow != null)
+                                {
+                                    throw rethrow;
+                                }
+                            }
+                            return Felix.m_secureAction.openJarFile(target);
+                        }
+                    }
+                    throw new IOException("Unable to access bundle revision.");
+                }
+
+                @Override
+                public void connect() throws IOException
+                {
+
+                }
+            };
+        }
+
+        private static URL create(BundleImpl bundle) throws MalformedURLException
+        {
+            String location = bundle._getLocation();
+
+            if (location.startsWith("reference:"))
+            {
+                location = location.substring("reference:".length());
+            }
+
+            BundleRevisionImpl revision = bundle.adapt(BundleRevisionImpl.class);
+            RevisionAsJarURL handler = new RevisionAsJarURL(revision);
+
+            URL url;
+            try
+            {
+                url = Felix.m_secureAction.createURL(
+                    Felix.m_secureAction.createURL(null, "jar:", handler), location, null);
+            }
+            catch (MalformedURLException ex)
+            {
+                url = null;
+            }
+
+            if (url != null && !url.getProtocol().equalsIgnoreCase("jar"))
+            {
+                return url;
+            }
+            else if (url == null)
+            {
+                location = "jar:" + revision.getEntry("/") + "!/";
+            }
+
+            return Felix.m_secureAction.createURL(
+                Felix.m_secureAction.createURL(null, "jar:", handler),
+                location,
+                handler
+            );
+        }
+    }
+
     private final WeakReference m_felix;
     private final WeakReference m_bundle;
     private final int m_hashCode;
     private final String m_toString;
     private final WeakReference m_revision;
 
-    // TODO: SECURITY - This should probably take a revision, not a bundle.
     BundleProtectionDomain(Felix felix, BundleImpl bundle, Object certificates)
         throws MalformedURLException
     {
         super(
             new CodeSource(
-                Felix.m_secureAction.createURL(
-                    Felix.m_secureAction.createURL(null, "location:", new FakeURLStreamHandler()),
-                    bundle._getLocation().startsWith("reference:") ? 
-                        bundle._getLocation().substring("reference:".length()) : 
-                        bundle._getLocation(),
-                    new FakeURLStreamHandler()
-                    ),
+                RevisionAsJarURL.create(bundle),
                 (Certificate[]) certificates),
             null, null, null);
         m_felix = new WeakReference(felix);

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java?rev=1628702&r1=1628701&r2=1628702&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java Wed Oct  1 13:49:05 2014
@@ -26,6 +26,7 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
+import java.util.jar.JarFile;
 import java.util.zip.ZipFile;
 
 import org.osgi.framework.Bundle;
@@ -573,6 +574,32 @@ public class SecureAction
         }
     }
 
+    public void deleteFileOnExit(File file)
+        throws IOException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.DELETE_FILEONEXIT_ACTION, file);
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof IOException)
+                {
+                    throw (IOException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            file.deleteOnExit();
+        }
+    }
+
     public URLConnection openURLConnection(URL url) throws IOException
     {
         if (System.getSecurityManager() != null)
@@ -624,6 +651,31 @@ public class SecureAction
         }
     }
 
+    public JarFile openJarFile(File file) throws IOException
+    {
+        if (System.getSecurityManager() != null)
+        {
+            try
+            {
+                Actions actions = (Actions) m_actions.get();
+                actions.set(Actions.OPEN_JARFILE_ACTION, file);
+                return (JarFile) AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException ex)
+            {
+                if (ex.getException() instanceof IOException)
+                {
+                    throw (IOException) ex.getException();
+                }
+                throw (RuntimeException) ex.getException();
+            }
+        }
+        else
+        {
+            return new JarFile(file);
+        }
+    }
+
     public void startActivator(BundleActivator activator, BundleContext context)
         throws Exception
     {
@@ -1453,6 +1505,8 @@ public class SecureAction
         public static final int INVOKE_RESOLVER_HOOK_MATCHES = 51;
         public static final int INVOKE_RESOLVER_HOOK_END = 52;
         public static final int INVOKE_BUNDLE_COLLISION_HOOK = 53;
+        public static final int OPEN_JARFILE_ACTION = 54;
+        public static final int DELETE_FILEONEXIT_ACTION = 55;
 
         private int m_action = -1;
         private Object m_arg1 = null;
@@ -1702,6 +1756,11 @@ public class SecureAction
                     ((org.osgi.framework.hooks.bundle.CollisionHook) arg1).filterCollisions((Integer) arg2,
                         (Bundle) arg3, (Collection<Bundle>) arg4);
                     return null;
+                case OPEN_JARFILE_ACTION:
+                    return new JarFile((File) arg1);
+                case DELETE_FILEONEXIT_ACTION:
+                    ((File) arg1).deleteOnExit();
+                    return null;
             }
 
             return null;