src/jdk/internal/dynalink/beans/CheckRestrictedPackageInternal.java

Mon, 18 Feb 2013 16:00:15 +0100

author
attila
date
Mon, 18 Feb 2013 16:00:15 +0100
changeset 101
f8221ce53c2e
parent 90
5a820fb11814
permissions
-rw-r--r--

8008371: Fix Dynalink compiler warnings and whitespace
Reviewed-by: jlaskey, sundar

attila@90 1 /*
attila@90 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
attila@90 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
attila@90 4 *
attila@90 5 * This code is free software; you can redistribute it and/or modify it
attila@90 6 * under the terms of the GNU General Public License version 2 only, as
attila@90 7 * published by the Free Software Foundation. Oracle designates this
attila@90 8 * particular file as subject to the "Classpath" exception as provided
attila@90 9 * by Oracle in the LICENSE file that accompanied this code.
attila@90 10 *
attila@90 11 * This code is distributed in the hope that it will be useful, but WITHOUT
attila@90 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
attila@90 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
attila@90 14 * version 2 for more details (a copy is included in the LICENSE file that
attila@90 15 * accompanied this code).
attila@90 16 *
attila@90 17 * You should have received a copy of the GNU General Public License version
attila@90 18 * 2 along with this work; if not, write to the Free Software Foundation,
attila@90 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
attila@90 20 *
attila@90 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
attila@90 22 * or visit www.oracle.com if you need additional information or have any
attila@90 23 * questions.
attila@90 24 */
attila@90 25
attila@90 26 /*
attila@90 27 * This file is available under and governed by the GNU General Public
attila@90 28 * License version 2 only, as published by the Free Software Foundation.
attila@90 29 * However, the following notice accompanied the original version of this
attila@90 30 * file, and Oracle licenses the original version of this file under the BSD
attila@90 31 * license:
attila@90 32 */
attila@90 33 /*
attila@90 34 Copyright 2009-2013 Attila Szegedi
attila@90 35
attila@90 36 Licensed under both the Apache License, Version 2.0 (the "Apache License")
attila@90 37 and the BSD License (the "BSD License"), with licensee being free to
attila@90 38 choose either of the two at their discretion.
attila@90 39
attila@90 40 You may not use this file except in compliance with either the Apache
attila@90 41 License or the BSD License.
attila@90 42
attila@90 43 If you choose to use this file in compliance with the Apache License, the
attila@90 44 following notice applies to you:
attila@90 45
attila@90 46 You may obtain a copy of the Apache License at
attila@90 47
attila@90 48 http://www.apache.org/licenses/LICENSE-2.0
attila@90 49
attila@90 50 Unless required by applicable law or agreed to in writing, software
attila@90 51 distributed under the License is distributed on an "AS IS" BASIS,
attila@90 52 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
attila@90 53 implied. See the License for the specific language governing
attila@90 54 permissions and limitations under the License.
attila@90 55
attila@90 56 If you choose to use this file in compliance with the BSD License, the
attila@90 57 following notice applies to you:
attila@90 58
attila@90 59 Redistribution and use in source and binary forms, with or without
attila@90 60 modification, are permitted provided that the following conditions are
attila@90 61 met:
attila@90 62 * Redistributions of source code must retain the above copyright
attila@90 63 notice, this list of conditions and the following disclaimer.
attila@90 64 * Redistributions in binary form must reproduce the above copyright
attila@90 65 notice, this list of conditions and the following disclaimer in the
attila@90 66 documentation and/or other materials provided with the distribution.
attila@90 67 * Neither the name of the copyright holder nor the names of
attila@90 68 contributors may be used to endorse or promote products derived from
attila@90 69 this software without specific prior written permission.
attila@90 70
attila@90 71 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
attila@90 72 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
attila@90 73 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
attila@90 74 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
attila@90 75 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
attila@90 76 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
attila@90 77 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
attila@90 78 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
attila@90 79 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
attila@90 80 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
attila@90 81 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
attila@90 82 */
attila@90 83
attila@90 84 package jdk.internal.dynalink.beans;
attila@90 85
attila@90 86 import java.io.ByteArrayOutputStream;
attila@90 87 import java.io.IOException;
attila@90 88 import java.io.InputStream;
attila@90 89 import java.lang.invoke.MethodHandle;
attila@90 90 import java.lang.invoke.MethodHandles;
attila@90 91 import java.lang.reflect.Method;
attila@90 92 import java.security.AccessController;
attila@90 93 import java.security.Permissions;
attila@90 94 import java.security.PrivilegedAction;
attila@90 95 import java.security.ProtectionDomain;
attila@90 96 import java.security.SecureClassLoader;
attila@90 97
attila@90 98 /**
attila@90 99 * A utility class to check whether a given class is in a package with restricted access e.g. "sun.*". These packages
attila@90 100 * are normally listed in the security property "package.access" for most JRE implementations, although we fortunately
attila@90 101 * don't rely on it but solely on {@link SecurityManager#checkPackageAccess(String)}).
attila@90 102 *
attila@90 103 * This class accomplishes the check in a fashion that works reliably even if Dynalink itself (and all the code on the
attila@90 104 * stack that led to the invocation) has the permission to access the restricted package.
attila@90 105 *
attila@90 106 * If Dynalink has a broad set of privileges (notably, it is loaded from boot or extension class path), then it loads
attila@90 107 * the {@link RestrictedPackageTester} class into a isolated secure class loader that gives it no permissions
attila@90 108 * whatsoever, and uses this completely unprivileged class to subsequently invoke
attila@90 109 * {@link SecurityManager#checkPackageAccess(String)}. This will reliably throw a {@link SecurityException} for every
attila@90 110 * restricted package even if Dynalink and other code on the stack have the requisite {@code "accessClassInPackage.*"}
attila@90 111 * {@link RuntimePermission} privilege.
attila@90 112 *
attila@90 113 * On the other hand, if Dynalink does not have a broad set of privileges normally granted by the boot or extension
attila@90 114 * class path, it will probably lack the privilege to create a new secure class loader into which to load the tester
attila@90 115 * class. In this case, it will invoke {@link SecurityManager#checkPackageAccess(String)} itself with the reasoning that
attila@90 116 * it will also be sufficient to discover whether a package is restricted or not.
attila@90 117 *
attila@90 118 * The rationale for this design is that if Dynalink is running as part of a privileged classpath - boot or extension
attila@90 119 * class path, it will have all privileges, so a security manager's package access check might succeed if all other code
attila@90 120 * on the stack when requesting linking with a particular restricted class is also privileged. A subsequent linking
attila@90 121 * request from less privileged code would then also succeed in requesting methods in privileged package. On the other
attila@90 122 * hand, if Dynalink is privileged, it will be able to delegate the package access check to the unprivileged class and
attila@90 123 * narrow the access based on its result. Finally, if Dynalink itself is unprivileged, it will not be able to load the
attila@90 124 * unprivileged class, but then it will also fail the security manager's package access.
attila@90 125 *
attila@90 126 * With this design, Dynalink effectively restrains itself from giving unauthorized access to restricted packages from
attila@90 127 * classes doing the linking in case it itself has access to those packages. The only way to defeat it would be to
attila@90 128 * selectively give Dynalink some {@code "accessClassInPackage.*"} permissions while denying it the privilege to
attila@90 129 * manipulate class loaders.
attila@90 130 */
attila@90 131 class CheckRestrictedPackageInternal {
attila@90 132 private static final MethodHandle PACKAGE_ACCESS_CHECK = getPackageAccessCheckMethod();
attila@90 133 private static final String TESTER_CLASS_NAME = "jdk.internal.dynalink.beans.RestrictedPackageTester";
attila@90 134
attila@90 135 /**
attila@90 136 * Returns true if the specified package has restricted access.
attila@90 137 * @param pkgName the name of the package to check.
attila@90 138 * @return true if the specified package has restricted access, false otherwise.
attila@90 139 * @throws NullPointerException if pkgName is null, or if there is {@link System#getSecurityManager()} returns null
attila@90 140 * as this method is only expected to be invoked in the presence of a security manager.
attila@90 141 */
attila@90 142 static boolean isRestrictedPackageName(String pkgName) {
attila@90 143 try {
attila@90 144 if(PACKAGE_ACCESS_CHECK != null) {
attila@90 145 // If we were able to load our unprivileged tester class, use it to check package access
attila@90 146 try {
attila@90 147 PACKAGE_ACCESS_CHECK.invokeExact(pkgName);
attila@90 148 } catch(Error|RuntimeException e) {
attila@90 149 throw e;
attila@90 150 } catch(Throwable t) {
attila@90 151 throw new RuntimeException(t);
attila@90 152 }
attila@90 153 } else {
attila@90 154 // If we didn't have sufficient permissions to load our unprivileged tester class, we're definitely not
attila@90 155 // running in a privileged class path, so invoking SecurityManager.checkPackageAccess() directly should
attila@90 156 // have the same effect as going through an unprivileged tester.
attila@90 157 System.getSecurityManager().checkPackageAccess(pkgName);
attila@90 158 }
attila@90 159 return false;
attila@90 160 } catch(SecurityException e) {
attila@90 161 return true;
attila@90 162 }
attila@90 163 }
attila@90 164
attila@90 165 private static MethodHandle getPackageAccessCheckMethod() {
attila@90 166 try {
attila@90 167 return AccessController.doPrivileged(new PrivilegedAction<MethodHandle>() {
attila@90 168 @Override
attila@90 169 public MethodHandle run() {
attila@90 170 return getPackageAccessCheckMethodInternal();
attila@90 171 }
attila@90 172 });
attila@90 173 } catch(SecurityException e) {
attila@90 174 // We don't have sufficient privileges to load our tester class into a separate protection domain, so just
attila@90 175 // return null so isRestrictedPackageName() will default to itself invoking
attila@90 176 // SecurityManager.checkPackageAccess().
attila@90 177 return null;
attila@90 178 }
attila@90 179 }
attila@90 180
attila@90 181 static MethodHandle getPackageAccessCheckMethodInternal() {
attila@90 182 try {
attila@90 183 // Can't use MethodHandles.lookup().findStatic() -- even though both this class and the loaded class are in
attila@90 184 // the same package, findStatic() will throw an IllegalAccessException since they have different class
attila@90 185 // loaders. That's why we have to use unreflect with a setAccessible(true)...
attila@90 186 final Method m = getTesterClass().getDeclaredMethod("checkPackageAccess", String.class);
attila@90 187 m.setAccessible(true);
attila@90 188 return MethodHandles.lookup().unreflect(m);
attila@90 189 } catch(IllegalAccessException|NoSuchMethodException e) {
attila@90 190 throw new AssertionError(e);
attila@90 191 }
attila@90 192 }
attila@90 193
attila@90 194 private static Class<?> getTesterClass() {
attila@90 195 final ClassLoader loader = getTesterClassLoader();
attila@90 196 try {
attila@90 197 final Class<?> checkerClass = Class.forName(TESTER_CLASS_NAME, true, loader);
attila@90 198 // Sanity check to ensure we didn't accidentally pick up the class from elsewhere
attila@90 199 if(checkerClass.getClassLoader() != loader) {
attila@90 200 throw new AssertionError(TESTER_CLASS_NAME + " was loaded from a different class loader");
attila@90 201 }
attila@90 202 return checkerClass;
attila@90 203 } catch(ClassNotFoundException e) {
attila@90 204 throw new AssertionError(e);
attila@90 205 }
attila@90 206 }
attila@90 207
attila@90 208 private static ClassLoader getTesterClassLoader() {
attila@90 209 // We deliberately override loadClass instead of findClass so that we don't give a chance to finding this
attila@90 210 // class already loaded anywhere else. Not that there's a big possibility for this, especially since the parent
attila@90 211 // class loader is the bootstrap class loader, but still...
attila@90 212 return new SecureClassLoader(null) {
attila@90 213
attila@90 214 @Override
attila@90 215 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
attila@90 216 if(name.equals(TESTER_CLASS_NAME)) {
attila@90 217 final byte[] bytes = getTesterClassBytes();
attila@90 218 // Define the class with a protection domain that grants no permissions.
attila@90 219 Class<?> clazz = defineClass(name, bytes, 0, bytes.length, new ProtectionDomain(null,
attila@90 220 new Permissions()));
attila@90 221 if(resolve) {
attila@90 222 resolveClass(clazz);
attila@90 223 }
attila@90 224 return clazz;
attila@90 225 }
attila@101 226
attila@101 227 return super.loadClass(name, resolve);
attila@90 228 }
attila@90 229 };
attila@90 230 }
attila@90 231
attila@101 232 static byte[] getTesterClassBytes() {
attila@90 233 try {
attila@90 234 final InputStream in = CheckRestrictedPackage.class.getResourceAsStream("RestrictedPackageTester.class");
attila@90 235 try {
attila@90 236 final ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
attila@90 237 for(;;) {
attila@90 238 final int b = in.read();
attila@90 239 if(b == -1) {
attila@90 240 break;
attila@90 241 }
attila@90 242 out.write(b);
attila@90 243 }
attila@90 244 return out.toByteArray();
attila@90 245 } finally {
attila@90 246 in.close();
attila@90 247 }
attila@90 248 } catch(IOException e) {
attila@90 249 throw new RuntimeException(e);
attila@90 250 }
attila@90 251 }
attila@90 252 }

mercurial