nashorn/src/jdk/internal/dynalink/beans/SandboxClassLoader.java
changeset 19232 ddfc1727d3ef
parent 19231 6f0ebdd538a0
parent 19136 bbe43d712fe0
child 19233 28e832b479d2
equal deleted inserted replaced
19231:6f0ebdd538a0 19232:ddfc1727d3ef
     1 /*
       
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 /*
       
    27  * This file is available under and governed by the GNU General Public
       
    28  * License version 2 only, as published by the Free Software Foundation.
       
    29  * However, the following notice accompanied the original version of this
       
    30  * file, and Oracle licenses the original version of this file under the BSD
       
    31  * license:
       
    32  */
       
    33 /*
       
    34    Copyright 2009-2013 Attila Szegedi
       
    35 
       
    36    Licensed under both the Apache License, Version 2.0 (the "Apache License")
       
    37    and the BSD License (the "BSD License"), with licensee being free to
       
    38    choose either of the two at their discretion.
       
    39 
       
    40    You may not use this file except in compliance with either the Apache
       
    41    License or the BSD License.
       
    42 
       
    43    If you choose to use this file in compliance with the Apache License, the
       
    44    following notice applies to you:
       
    45 
       
    46        You may obtain a copy of the Apache License at
       
    47 
       
    48            http://www.apache.org/licenses/LICENSE-2.0
       
    49 
       
    50        Unless required by applicable law or agreed to in writing, software
       
    51        distributed under the License is distributed on an "AS IS" BASIS,
       
    52        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
       
    53        implied. See the License for the specific language governing
       
    54        permissions and limitations under the License.
       
    55 
       
    56    If you choose to use this file in compliance with the BSD License, the
       
    57    following notice applies to you:
       
    58 
       
    59        Redistribution and use in source and binary forms, with or without
       
    60        modification, are permitted provided that the following conditions are
       
    61        met:
       
    62        * Redistributions of source code must retain the above copyright
       
    63          notice, this list of conditions and the following disclaimer.
       
    64        * Redistributions in binary form must reproduce the above copyright
       
    65          notice, this list of conditions and the following disclaimer in the
       
    66          documentation and/or other materials provided with the distribution.
       
    67        * Neither the name of the copyright holder nor the names of
       
    68          contributors may be used to endorse or promote products derived from
       
    69          this software without specific prior written permission.
       
    70 
       
    71        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
       
    72        IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
       
    73        TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
       
    74        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
       
    75        BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
       
    76        CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
       
    77        SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
       
    78        BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
       
    79        WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
       
    80        OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
       
    81        ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    82 */
       
    83 
       
    84 package jdk.internal.dynalink.beans;
       
    85 
       
    86 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
       
    87 import static jdk.internal.org.objectweb.asm.Opcodes.ASM4;
       
    88 
       
    89 import java.io.IOException;
       
    90 import java.io.InputStream;
       
    91 import java.security.Permissions;
       
    92 import java.security.ProtectionDomain;
       
    93 import java.security.SecureClassLoader;
       
    94 import java.security.SecureRandom;
       
    95 import jdk.internal.org.objectweb.asm.ClassReader;
       
    96 import jdk.internal.org.objectweb.asm.ClassVisitor;
       
    97 import jdk.internal.org.objectweb.asm.ClassWriter;
       
    98 import jdk.internal.org.objectweb.asm.MethodVisitor;
       
    99 
       
   100 /**
       
   101  * A utility class that can load a class with specified name into an isolated zero-permissions protection domain. It can
       
   102  * be used to load classes that perform security-sensitive operations with no privileges at all, therefore ensuring such
       
   103  * operations will only succeed if they would require no permissions, as well as to make sure that if these operations
       
   104  * bind some part of the security execution context to their results, the bound security context is completely
       
   105  * unprivileged. Such measures serve as firebreaks against accidental privilege escalation.
       
   106  */
       
   107 final class SandboxClassLoader {
       
   108     private final String className;
       
   109     private final String randomizedClassName;
       
   110 
       
   111     private SandboxClassLoader(String className) {
       
   112         this.className = className;
       
   113         final String simpleClassName = className.substring(className.lastIndexOf('.') + 1);
       
   114         this.randomizedClassName = "randomPackage" + Long.toHexString(new SecureRandom().nextLong()) + "." + simpleClassName;
       
   115     }
       
   116 
       
   117     /**
       
   118      * Load the named class into a zero-permissions protection domain. Even if the class is already loaded into the
       
   119      * Dynalink's class loader, an independent class is created from the same bytecode, thus the returned class will
       
   120      * never be identical with the one that might already be loaded. The class to be loaded is supposed to be package
       
   121      * private and have no public constructors. This is not a functional requirement, but it is enforced to ensure that
       
   122      * the original class was made adequately inaccessible. The returned class will be public and its constructors will
       
   123      * be changed to public. The only permission given to the returned class will be
       
   124      * {@code accessClassInPackage.jdk.internal.dynalink.beans.sandbox}. That package should be used solely to define
       
   125      * SPI interfaces implemented by the loaded class.
       
   126      * @param className the fully qualified name of the class to load
       
   127      * @return the loaded class, renamed to a random package, made public, its constructors made public, and lacking any
       
   128      * permissions except access to the sandbox package.
       
   129      * @throws SecurityException if the calling code lacks the {@code createClassLoader} runtime permission. This
       
   130      * normally means that Dynalink itself is running as untrusted code, and whatever functionality was meant to be
       
   131      * isolated into an unprivileged class is likely okay to be used directly too.
       
   132      */
       
   133     static Class<?> loadClass(String className) throws SecurityException {
       
   134         return new SandboxClassLoader(className).loadClass();
       
   135     }
       
   136 
       
   137     private Class<?> loadClass() throws SecurityException {
       
   138         final ClassLoader loader = createClassLoader();
       
   139         try {
       
   140             final Class<?> clazz = Class.forName(randomizedClassName, true, loader);
       
   141             // Sanity check to ensure we didn't accidentally pick up the class from elsewhere
       
   142             if(clazz.getClassLoader() != loader) {
       
   143                 throw new AssertionError(randomizedClassName + " was loaded from a different class loader");
       
   144             }
       
   145             return clazz;
       
   146         } catch(ClassNotFoundException e) {
       
   147             throw new AssertionError(e);
       
   148         }
       
   149     }
       
   150 
       
   151     private ClassLoader createClassLoader() throws SecurityException {
       
   152         final String lclassName = this.randomizedClassName;
       
   153         // We deliberately override loadClass instead of findClass so that we don't give a chance to finding this
       
   154         // class already loaded anywhere else. We use this class' loader as the parent class loader as the loaded class
       
   155         // needs to be able to access implemented interfaces from the sandbox package.
       
   156         return new SecureClassLoader(getClass().getClassLoader()) {
       
   157             @Override
       
   158             protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
       
   159                 if(name.equals(lclassName)) {
       
   160                     final byte[] bytes = getClassBytes();
       
   161                     // Define the class with a protection domain that grants (almost) no permissions.
       
   162                     Class<?> clazz = defineClass(name, bytes, 0, bytes.length, createMinimalPermissionsDomain());
       
   163                     if(resolve) {
       
   164                         resolveClass(clazz);
       
   165                     }
       
   166                     return clazz;
       
   167                 }
       
   168 
       
   169                 final int i = name.lastIndexOf('.');
       
   170                 if (i != -1) {
       
   171                     final SecurityManager sm = System.getSecurityManager();
       
   172                     if (sm != null) {
       
   173                         sm.checkPackageAccess(name.substring(0, i));
       
   174                     }
       
   175                 }
       
   176                 return super.loadClass(name, resolve);
       
   177             }
       
   178         };
       
   179     }
       
   180 
       
   181     /**
       
   182      * Create a no-permissions protection domain. Except, it's not really a no-permissions protection domain, since we
       
   183      * need to give the protection domain the permission to access the sandbox package where the interop interfaces are
       
   184      * defined.
       
   185      * @return a new (almost) no-permission protection domain.
       
   186      */
       
   187     private static ProtectionDomain createMinimalPermissionsDomain() {
       
   188         final Permissions p = new Permissions();
       
   189         p.add(new RuntimePermission("accessClassInPackage.jdk.internal.dynalink.beans.sandbox"));
       
   190         return new ProtectionDomain(null, p);
       
   191     }
       
   192 
       
   193     private byte[] getClassBytes() {
       
   194         try(final InputStream in = getClass().getResourceAsStream("/" + className.replace('.', '/') + ".class")) {
       
   195             final ClassReader cr = new ClassReader(in);
       
   196             final ClassWriter cw = new ClassWriter(cr, 0);
       
   197             cr.accept(new ClassVisitor(ASM4, cw) {
       
   198                 @Override
       
   199                 public void visit(int version, int access, String name, String signature, String superName,
       
   200                         String[] interfaces) {
       
   201                     // Rename the class to its random name, and make it public (otherwise we won't be able to
       
   202                     // instantiate it). The privileged template class is package-private.
       
   203                     if((access & ACC_PUBLIC) != 0) {
       
   204                         throw new IllegalArgumentException("Class " + className + " must be package-private");
       
   205                     }
       
   206                     super.visit(version, access | ACC_PUBLIC, randomizedClassName.replace('.', '/'),
       
   207                             signature, superName, interfaces);
       
   208                 }
       
   209 
       
   210                 @Override
       
   211                 public MethodVisitor visitMethod(int access, String name, String desc, String signature,
       
   212                         String[] exceptions) {
       
   213                     // Make the constructor(s) public (otherwise we won't be able to instantiate the class). The
       
   214                     // privileged template's constructor(s) should not be public.
       
   215                     final boolean isCtor = "<init>".equals(name);
       
   216                     if(isCtor && ((access & ACC_PUBLIC) != 0)) {
       
   217                         throw new IllegalArgumentException("Class " + className + " must have no public constructors");
       
   218                     }
       
   219                     return super.visitMethod(isCtor ? (access | ACC_PUBLIC) : access, name, desc, signature,
       
   220                             exceptions);
       
   221                 }
       
   222             }, 0);
       
   223             return cw.toByteArray();
       
   224         } catch(IOException e) {
       
   225             throw new RuntimeException(e);
       
   226         }
       
   227     }
       
   228 }