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 }