1 /* 2 * Copyright (c) 1997, 2015, 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 package java.net; 27 28 import java.io.File; 29 import java.io.FilePermission; 30 import java.io.InputStream; 31 import java.io.IOException; 32 import java.util.Enumeration; 33 import java.util.NoSuchElementException; 34 import java.util.StringTokenizer; 35 import java.util.jar.Manifest; 36 import java.util.jar.Attributes; 37 import java.util.jar.Attributes.Name; 38 import java.security.CodeSigner; 39 import java.security.PrivilegedAction; 40 import java.security.PrivilegedExceptionAction; 41 import java.security.AccessController; 42 import java.security.AccessControlContext; 43 import java.security.SecureClassLoader; 44 import java.security.CodeSource; 45 import java.security.Permission; 46 import java.security.PermissionCollection; 47 import sun.misc.Resource; 48 import sun.misc.URLClassPath; 49 import sun.net.www.ParseUtil; 50 import sun.security.util.SecurityConstants; 51 52 /** 53 * This class loader is used to load classes and resources from a search 54 * path of URLs referring to both JAR files and directories. Any URL that 55 * ends with a '/' is assumed to refer to a directory. Otherwise, the URL 56 * is assumed to refer to a JAR file which will be opened as needed. 57 * <p> 58 * The AccessControlContext of the thread that created the instance of 59 * URLClassLoader will be used when subsequently loading classes and 60 * resources. 61 * <p> 62 * The classes that are loaded are by default granted permission only to 63 * access the URLs specified when the URLClassLoader was created. 64 * 65 * @author David Connelly 66 * @since 1.2 67 */ 68 public class URLClassLoader extends SecureClassLoader { 69 /* The search path for classes and resources */ 70 URLClassPath ucp; 71 72 /* The context to be used when loading classes and resources */ 73 private AccessControlContext acc; 74 75 /** 76 * Constructs a new URLClassLoader for the given URLs. The URLs will be 77 * searched in the order specified for classes and resources after first 78 * searching in the specified parent class loader. Any URL that ends with 79 * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed 80 * to refer to a JAR file which will be downloaded and opened as needed. 81 * 82 * <p>If there is a security manager, this method first 83 * calls the security manager's <code>checkCreateClassLoader</code> method 84 * to ensure creation of a class loader is allowed. 85 * 86 * @param urls the URLs from which to load classes and resources 87 * @param parent the parent class loader for delegation 88 * @exception SecurityException if a security manager exists and its 89 * <code>checkCreateClassLoader</code> method doesn't allow 90 * creation of a class loader. 91 * @see SecurityManager#checkCreateClassLoader 92 */ 93 public URLClassLoader(URL[] urls, ClassLoader parent) { 94 super(parent); 95 // this is to make the stack depth consistent with 1.1 96 SecurityManager security = System.getSecurityManager(); 97 if (security != null) { 98 security.checkCreateClassLoader(); 99 } 100 ucp = new URLClassPath(urls); 101 acc = AccessController.getContext(); 102 } 103 104 /** 105 * Constructs a new URLClassLoader for the specified URLs using the 106 * default delegation parent <code>ClassLoader</code>. The URLs will 107 * be searched in the order specified for classes and resources after 108 * first searching in the parent class loader. Any URL that ends with 109 * a '/' is assumed to refer to a directory. Otherwise, the URL is 110 * assumed to refer to a JAR file which will be downloaded and opened 111 * as needed. 112 * 113 * <p>If there is a security manager, this method first 114 * calls the security manager's <code>checkCreateClassLoader</code> method 115 * to ensure creation of a class loader is allowed. 116 * 117 * @param urls the URLs from which to load classes and resources 118 * 119 * @exception SecurityException if a security manager exists and its 120 * <code>checkCreateClassLoader</code> method doesn't allow 121 * creation of a class loader. 122 * @see SecurityManager#checkCreateClassLoader 123 */ 124 public URLClassLoader(URL[] urls) { 125 super(); 126 // this is to make the stack depth consistent with 1.1 127 SecurityManager security = System.getSecurityManager(); 128 if (security != null) { 129 security.checkCreateClassLoader(); 130 } 131 ucp = new URLClassPath(urls); 132 acc = AccessController.getContext(); 133 } 134 135 /** 136 * Constructs a new URLClassLoader for the specified URLs, parent 137 * class loader, and URLStreamHandlerFactory. The parent argument 138 * will be used as the parent class loader for delegation. The 139 * factory argument will be used as the stream handler factory to 140 * obtain protocol handlers when creating new jar URLs. 141 * 142 * <p>If there is a security manager, this method first 143 * calls the security manager's <code>checkCreateClassLoader</code> method 144 * to ensure creation of a class loader is allowed. 145 * 146 * @param urls the URLs from which to load classes and resources 147 * @param parent the parent class loader for delegation 148 * @param factory the URLStreamHandlerFactory to use when creating URLs 149 * 150 * @exception SecurityException if a security manager exists and its 151 * <code>checkCreateClassLoader</code> method doesn't allow 152 * creation of a class loader. 153 * @see SecurityManager#checkCreateClassLoader 154 */ 155 public URLClassLoader(URL[] urls, ClassLoader parent, 156 URLStreamHandlerFactory factory) { 157 super(parent); 158 // this is to make the stack depth consistent with 1.1 159 SecurityManager security = System.getSecurityManager(); 160 if (security != null) { 161 security.checkCreateClassLoader(); 162 } 163 ucp = new URLClassPath(urls, factory); 164 acc = AccessController.getContext(); 165 } 166 167 /** 168 * Appends the specified URL to the list of URLs to search for 169 * classes and resources. 170 * 171 * @param url the URL to be added to the search path of URLs 172 */ 173 protected void addURL(URL url) { 174 ucp.addURL(url); 175 } 176 177 /** 178 * Returns the search path of URLs for loading classes and resources. 179 * This includes the original list of URLs specified to the constructor, 180 * along with any URLs subsequently appended by the addURL() method. 181 * @return the search path of URLs for loading classes and resources. 182 */ 183 public URL[] getURLs() { 184 return ucp.getURLs(); 185 } 186 187 /** 188 * Finds and loads the class with the specified name from the URL search 189 * path. Any URLs referring to JAR files are loaded and opened as needed 190 * until the class is found. 191 * 192 * @param name the name of the class 193 * @return the resulting class 194 * @exception ClassNotFoundException if the class could not be found 195 */ 196 protected Class<?> findClass(final String name) 197 throws ClassNotFoundException 198 { 199 try { 200 return AccessController.doPrivileged( 201 new PrivilegedExceptionAction<Class<?>>() { 202 public Class<?> run() throws ClassNotFoundException { 203 String path = name.replace('.', '/').concat(".class"); 204 Resource res = ucp.getResource(path, false); 205 if (res != null) { 206 try { 207 return defineClass(name, res); 208 } catch (IOException e) { 209 throw new ClassNotFoundException(name, e); 210 } 211 } else { 212 throw new ClassNotFoundException(name); 213 } 214 } 215 }, acc); 216 } catch (java.security.PrivilegedActionException pae) { 217 throw (ClassNotFoundException) pae.getException(); 218 } 219 } 220 221 /* 222 * Defines a Class using the class bytes obtained from the specified 223 * Resource. The resulting Class must be resolved before it can be 224 * used. 225 */ 226 private Class<?> defineClass(String name, Resource res) throws IOException { 227 int i = name.lastIndexOf('.'); 228 URL url = res.getCodeSourceURL(); 229 if (i != -1) { 230 String pkgname = name.substring(0, i); 231 // Check if package already loaded. 232 Package pkg = getPackage(pkgname); 233 Manifest man = res.getManifest(); 234 if (pkg != null) { 235 // Package found, so check package sealing. 236 if (pkg.isSealed()) { 237 // Verify that code source URL is the same. 238 if (!pkg.isSealed(url)) { 239 throw new SecurityException( 240 "sealing violation: package " + pkgname + " is sealed"); 241 } 242 243 } else { 244 // Make sure we are not attempting to seal the package 245 // at this code source URL. 246 if ((man != null) && isSealed(pkgname, man)) { 247 throw new SecurityException( 248 "sealing violation: can't seal package " + pkgname + 249 ": already loaded"); 250 } 251 } 252 } else { 253 if (man != null) { 254 definePackage(pkgname, man, url); 255 } else { 256 definePackage(pkgname, null, null, null, null, null, null, null); 257 } 258 } 259 } 260 // Now read the class bytes and define the class 261 java.nio.ByteBuffer bb = res.getByteBuffer(); 262 if (bb != null) { 263 // Use (direct) ByteBuffer: 264 CodeSigner[] signers = res.getCodeSigners(); 265 CodeSource cs = new CodeSource(url, signers); 266 return defineClass(name, bb, cs); 267 } else { 268 byte[] b = res.getBytes(); 269 // must read certificates AFTER reading bytes. 270 CodeSigner[] signers = res.getCodeSigners(); 271 CodeSource cs = new CodeSource(url, signers); 272 return defineClass(name, b, 0, b.length, cs); 273 } 274 } 275 276 /** 277 * Defines a new package by name in this ClassLoader. The attributes 278 * contained in the specified Manifest will be used to obtain package 279 * version and sealing information. For sealed packages, the additional 280 * URL specifies the code source URL from which the package was loaded. 281 * 282 * @param name the package name 283 * @param man the Manifest containing package version and sealing 284 * information 285 * @param url the code source url for the package, or null if none 286 * @exception IllegalArgumentException if the package name duplicates 287 * an existing package either in this class loader or one 288 * of its ancestors 289 * @return the newly defined Package object 290 */ 291 protected Package definePackage(String name, Manifest man, URL url) 292 throws IllegalArgumentException 293 { 294 String path = name.replace('.', '/').concat("/"); 295 String specTitle = null, specVersion = null, specVendor = null; 296 String implTitle = null, implVersion = null, implVendor = null; 297 String sealed = null; 298 URL sealBase = null; 299 300 Attributes attr = man.getAttributes(path); 301 if (attr != null) { 302 specTitle = attr.getValue(Name.SPECIFICATION_TITLE); 303 specVersion = attr.getValue(Name.SPECIFICATION_VERSION); 304 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); 305 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); 306 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); 307 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); 308 sealed = attr.getValue(Name.SEALED); 309 } 310 attr = man.getMainAttributes(); 311 if (attr != null) { 312 if (specTitle == null) { 313 specTitle = attr.getValue(Name.SPECIFICATION_TITLE); 314 } 315 if (specVersion == null) { 316 specVersion = attr.getValue(Name.SPECIFICATION_VERSION); 317 } 318 if (specVendor == null) { 319 specVendor = attr.getValue(Name.SPECIFICATION_VENDOR); 320 } 321 if (implTitle == null) { 322 implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE); 323 } 324 if (implVersion == null) { 325 implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION); 326 } 327 if (implVendor == null) { 328 implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR); 329 } 330 if (sealed == null) { 331 sealed = attr.getValue(Name.SEALED); 332 } 333 } 334 if ("true".equalsIgnoreCase(sealed)) { 335 sealBase = url; 336 } 337 return definePackage(name, specTitle, specVersion, specVendor, 338 implTitle, implVersion, implVendor, sealBase); 339 } 340 341 /* 342 * Returns true if the specified package name is sealed according to the 343 * given manifest. 344 */ 345 private boolean isSealed(String name, Manifest man) { 346 String path = name.replace('.', '/').concat("/"); 347 Attributes attr = man.getAttributes(path); 348 String sealed = null; 349 if (attr != null) { 350 sealed = attr.getValue(Name.SEALED); 351 } 352 if (sealed == null) { 353 if ((attr = man.getMainAttributes()) != null) { 354 sealed = attr.getValue(Name.SEALED); 355 } 356 } 357 return "true".equalsIgnoreCase(sealed); 358 } 359 360 /** 361 * Finds the resource with the specified name on the URL search path. 362 * 363 * @param name the name of the resource 364 * @return a <code>URL</code> for the resource, or <code>null</code> 365 * if the resource could not be found. 366 */ 367 public URL findResource(final String name) { 368 /* 369 * The same restriction to finding classes applies to resources 370 */ 371 URL url = AccessController.doPrivileged( 372 new PrivilegedAction<URL>() { 373 public URL run() { 374 return ucp.findResource(name, true); 375 } 376 }, acc); 377 378 return url != null ? ucp.checkURL(url) : null; 379 } 380 381 /** 382 * Returns an Enumeration of URLs representing all of the resources 383 * on the URL search path having the specified name. 384 * 385 * @param name the resource name 386 * @exception IOException if an I/O exception occurs 387 * @return an <code>Enumeration</code> of <code>URL</code>s 388 */ 389 public Enumeration<URL> findResources(final String name) 390 throws IOException 391 { 392 final Enumeration<URL> e = ucp.findResources(name, true); 393 394 return new Enumeration<URL>() { 395 private URL url = null; 396 397 private boolean next() { 398 if (url != null) { 399 return true; 400 } 401 do { 402 URL u = AccessController.doPrivileged( 403 new PrivilegedAction<URL>() { 404 public URL run() { 405 if (!e.hasMoreElements()) 406 return null; 407 return e.nextElement(); 408 } 409 }, acc); 410 if (u == null) 411 break; 412 url = ucp.checkURL(u); 413 } while (url == null); 414 return url != null; 415 } 416 417 public URL nextElement() { 418 if (!next()) { 419 throw new NoSuchElementException(); 420 } 421 URL u = url; 422 url = null; 423 return u; 424 } 425 426 public boolean hasMoreElements() { 427 return next(); 428 } 429 }; 430 } 431 432 /** 433 * Returns the permissions for the given codesource object. 434 * The implementation of this method first calls super.getPermissions 435 * and then adds permissions based on the URL of the codesource. 436 * <p> 437 * If the protocol of this URL is "jar", then the permission granted 438 * is based on the permission that is required by the URL of the Jar 439 * file. 440 * <p> 441 * If the protocol is "file" 442 * and the path specifies a file, then permission to read that 443 * file is granted. If protocol is "file" and the path is 444 * a directory, permission is granted to read all files 445 * and (recursively) all files and subdirectories contained in 446 * that directory. 447 * <p> 448 * If the protocol is not "file", then permission 449 * to connect to and accept connections from the URL's host is granted. 450 * @param codesource the codesource 451 * @return the permissions granted to the codesource 452 */ 453 protected PermissionCollection getPermissions(CodeSource codesource) 454 { 455 PermissionCollection perms = super.getPermissions(codesource); 456 457 URL url = codesource.getLocation(); 458 459 Permission p; 460 URLConnection urlConnection; 461 462 try { 463 urlConnection = url.openConnection(); 464 p = urlConnection.getPermission(); 465 } catch (java.io.IOException ioe) { 466 p = null; 467 urlConnection = null; 468 } 469 470 if (p instanceof FilePermission) { 471 // if the permission has a separator char on the end, 472 // it means the codebase is a directory, and we need 473 // to add an additional permission to read recursively 474 String path = p.getName(); 475 if (path.endsWith(File.separator)) { 476 path += "-"; 477 p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION); 478 } 479 } else if ((p == null) && (url.getProtocol().equals("file"))) { 480 String path = url.getFile().replace('/', File.separatorChar); 481 path = ParseUtil.decode(path); 482 if (path.endsWith(File.separator)) 483 path += "-"; 484 p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION); 485 } else { 486 /** 487 * Not loading from a 'file:' URL so we want to give the class 488 * permission to connect to and accept from the remote host 489 * after we've made sure the host is the correct one and is valid. 490 */ 491 URL locUrl = url; 492 if (urlConnection instanceof JarURLConnection) { 493 locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); 494 } 495 String host = locUrl.getHost(); 496 if (host != null && (host.length() > 0)) 497 p = new SocketPermission(host, 498 SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION); 499 } 500 501 // make sure the person that created this class loader 502 // would have this permission 503 504 if (p != null) { 505 final SecurityManager sm = System.getSecurityManager(); 506 if (sm != null) { 507 final Permission fp = p; 508 AccessController.doPrivileged(new PrivilegedAction<Void>() { 509 public Void run() throws SecurityException { 510 sm.checkPermission(fp); 511 return null; 512 } 513 }, acc); 514 } 515 perms.add(p); 516 } 517 return perms; 518 } 519 520 /** 521 * Creates a new instance of URLClassLoader for the specified 522 * URLs and parent class loader. If a security manager is 523 * installed, the <code>loadClass</code> method of the URLClassLoader 524 * returned by this method will invoke the 525 * <code>SecurityManager.checkPackageAccess</code> method before 526 * loading the class. 527 * 528 * @param urls the URLs to search for classes and resources 529 * @param parent the parent class loader for delegation 530 * @return the resulting class loader 531 */ 532 public static URLClassLoader newInstance(final URL[] urls, 533 final ClassLoader parent) { 534 // Save the caller's context 535 AccessControlContext acc = AccessController.getContext(); 536 // Need a privileged block to create the class loader 537 URLClassLoader ucl = AccessController.doPrivileged( 538 new PrivilegedAction<URLClassLoader>() { 539 public URLClassLoader run() { 540 return new FactoryURLClassLoader(urls, parent); 541 } 542 }); 543 // Now set the context on the loader using the one we saved, 544 // not the one inside the privileged block... 545 ucl.acc = acc; 546 return ucl; 547 } 548 549 /** 550 * Creates a new instance of URLClassLoader for the specified 551 * URLs and default parent class loader. If a security manager is 552 * installed, the <code>loadClass</code> method of the URLClassLoader 553 * returned by this method will invoke the 554 * <code>SecurityManager.checkPackageAccess</code> before 555 * loading the class. 556 * 557 * @param urls the URLs to search for classes and resources 558 * @return the resulting class loader 559 */ 560 public static URLClassLoader newInstance(final URL[] urls) { 561 // Save the caller's context 562 AccessControlContext acc = AccessController.getContext(); 563 // Need a privileged block to create the class loader 564 URLClassLoader ucl = AccessController.doPrivileged( 565 new PrivilegedAction<URLClassLoader>() { 566 public URLClassLoader run() { 567 return new FactoryURLClassLoader(urls); 568 } 569 }); 570 571 // Now set the context on the loader using the one we saved, 572 // not the one inside the privileged block... 573 ucl.acc = acc; 574 return ucl; 575 } 576 577 static { 578 sun.misc.SharedSecrets.setJavaNetAccess ( 579 new sun.misc.JavaNetAccess() { 580 public URLClassPath getURLClassPath (URLClassLoader u) { 581 return u.ucp; 582 } 583 584 public String getOriginalHostName(InetAddress ia) { 585 return ia.holder.getOriginalHostName(); 586 } 587 } 588 ); 589 } 590 } 591 592 final class FactoryURLClassLoader extends URLClassLoader { 593 594 FactoryURLClassLoader(URL[] urls, ClassLoader parent) { 595 super(urls, parent); 596 } 597 598 FactoryURLClassLoader(URL[] urls) { 599 super(urls); 600 } 601 602 public final synchronized Class<?> loadClass(String name, boolean resolve) 603 throws ClassNotFoundException 604 { 605 // First check if we have permission to access the package. This 606 // should go away once we've added support for exported packages. 607 SecurityManager sm = System.getSecurityManager(); 608 if (sm != null) { 609 int i = name.lastIndexOf('.'); 610 if (i != -1) { 611 sm.checkPackageAccess(name.substring(0, i)); 612 } 613 } 614 return super.loadClass(name, resolve); 615 } 616 }