1 /*
   2  * Copyright (c) 1998, 2014, 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 sun.misc;
  27 
  28 import java.io.File;
  29 import java.io.IOException;
  30 import java.io.FilePermission;
  31 import java.net.URL;
  32 import java.net.URLClassLoader;
  33 import java.net.MalformedURLException;
  34 import java.net.URLStreamHandler;
  35 import java.net.URLStreamHandlerFactory;
  36 import java.util.HashSet;
  37 import java.util.StringTokenizer;
  38 import java.util.Set;
  39 import java.util.Vector;
  40 import java.security.AccessController;
  41 import java.security.PrivilegedAction;
  42 import java.security.PrivilegedExceptionAction;
  43 import java.security.AccessControlContext;
  44 import java.security.PermissionCollection;
  45 import java.security.Permissions;
  46 import java.security.Permission;
  47 import java.security.ProtectionDomain;
  48 import java.security.CodeSource;
  49 import sun.security.util.SecurityConstants;
  50 import sun.net.www.ParseUtil;
  51 
  52 /**
  53  * This class is used by the system to launch the main application.
  54 Launcher */
  55 public class Launcher {
  56     private static URLStreamHandlerFactory factory = new Factory();
  57     private static Launcher launcher = new Launcher();
  58     private static String bootClassPath =
  59         System.getProperty("sun.boot.class.path");
  60 
  61     public static Launcher getLauncher() {
  62         return launcher;
  63     }
  64 
  65     private ClassLoader loader;
  66 
  67     public Launcher() {
  68         // Create the extension class loader
  69         ClassLoader extcl;
  70         try {
  71             extcl = ExtClassLoader.getExtClassLoader();
  72         } catch (IOException e) {
  73             throw new InternalError(
  74                 "Could not create extension class loader", e);
  75         }
  76 
  77         // Now create the class loader to use to launch the application
  78         try {
  79             loader = AppClassLoader.getAppClassLoader(extcl);
  80         } catch (IOException e) {
  81             throw new InternalError(
  82                 "Could not create application class loader", e);
  83         }
  84 
  85         // Also set the context class loader for the primordial thread.
  86         Thread.currentThread().setContextClassLoader(loader);
  87 
  88         // Finally, install a security manager if requested
  89         String s = System.getProperty("java.security.manager");
  90         if (s != null) {
  91             SecurityManager sm = null;
  92             if ("".equals(s) || "default".equals(s)) {
  93                 sm = new java.lang.SecurityManager();
  94             } else {
  95                 try {
  96                     sm = (SecurityManager)loader.loadClass(s).newInstance();
  97                 } catch (IllegalAccessException e) {
  98                 } catch (InstantiationException e) {
  99                 } catch (ClassNotFoundException e) {
 100                 } catch (ClassCastException e) {
 101                 }
 102             }
 103             if (sm != null) {
 104                 System.setSecurityManager(sm);
 105             } else {
 106                 throw new InternalError(
 107                     "Could not create SecurityManager: " + s);
 108             }
 109         }
 110     }
 111 
 112     /*
 113      * Returns the class loader used to launch the main application.
 114      */
 115     public ClassLoader getClassLoader() {
 116         return loader;
 117     }
 118 
 119     /*
 120      * The class loader used for loading installed extensions.
 121      */
 122     static class ExtClassLoader extends URLClassLoader {
 123 
 124         static {
 125             ClassLoader.registerAsParallelCapable();
 126         }
 127 
 128         /**
 129          * create an ExtClassLoader. The ExtClassLoader is created
 130          * within a context that limits which files it can read
 131          */
 132         public static ExtClassLoader getExtClassLoader() throws IOException
 133         {
 134             final File[] dirs = getExtDirs();
 135 
 136             try {
 137                 // Prior implementations of this doPrivileged() block supplied
 138                 // aa synthesized ACC via a call to the private method
 139                 // ExtClassLoader.getContext().
 140 
 141                 return AccessController.doPrivileged(
 142                     new PrivilegedExceptionAction<ExtClassLoader>() {
 143                         public ExtClassLoader run() throws IOException {
 144                             int len = dirs.length;
 145                             for (int i = 0; i < len; i++) {
 146                                 MetaIndex.registerDirectory(dirs[i]);
 147                             }
 148                             return new ExtClassLoader(dirs);
 149                         }
 150                     });
 151             } catch (java.security.PrivilegedActionException e) {
 152                 throw (IOException) e.getException();
 153             }
 154         }
 155 
 156         void addExtURL(URL url) {
 157             super.addURL(url);
 158         }
 159 
 160         /*
 161          * Creates a new ExtClassLoader for the specified directories.
 162          */
 163         public ExtClassLoader(File[] dirs) throws IOException {
 164             super(getExtURLs(dirs), null, factory);
 165             SharedSecrets.getJavaNetAccess().
 166                 getURLClassPath(this).initLookupCache(this);
 167         }
 168 
 169         private static File[] getExtDirs() {
 170             String s = System.getProperty("java.ext.dirs");
 171             File[] dirs;
 172             if (s != null) {
 173                 StringTokenizer st =
 174                     new StringTokenizer(s, File.pathSeparator);
 175                 int count = st.countTokens();
 176                 dirs = new File[count];
 177                 for (int i = 0; i < count; i++) {
 178                     dirs[i] = new File(st.nextToken());
 179                 }
 180             } else {
 181                 dirs = new File[0];
 182             }
 183             return dirs;
 184         }
 185 
 186         private static URL[] getExtURLs(File[] dirs) throws IOException {
 187             Vector<URL> urls = new Vector<URL>();
 188             for (int i = 0; i < dirs.length; i++) {
 189                 String[] files = dirs[i].list();
 190                 if (files != null) {
 191                     for (int j = 0; j < files.length; j++) {
 192                         if (!files[j].equals("meta-index")) {
 193                             File f = new File(dirs[i], files[j]);
 194                             urls.add(getFileURL(f));
 195                         }
 196                     }
 197                 }
 198             }
 199             URL[] ua = new URL[urls.size()];
 200             urls.copyInto(ua);
 201             return ua;
 202         }
 203 
 204         /*
 205          * Searches the installed extension directories for the specified
 206          * library name. For each extension directory, we first look for
 207          * the native library in the subdirectory whose name is the value
 208          * of the system property <code>os.arch</code>. Failing that, we
 209          * look in the extension directory itself.
 210          */
 211         public String findLibrary(String name) {
 212             name = System.mapLibraryName(name);
 213             URL[] urls = super.getURLs();
 214             File prevDir = null;
 215             for (int i = 0; i < urls.length; i++) {
 216                 // Get the ext directory from the URL
 217                 File dir = new File(urls[i].getPath()).getParentFile();
 218                 if (dir != null && !dir.equals(prevDir)) {
 219                     // Look in architecture-specific subdirectory first
 220                     // Read from the saved system properties to avoid deadlock
 221                     String arch = VM.getSavedProperty("os.arch");
 222                     if (arch != null) {
 223                         File file = new File(new File(dir, arch), name);
 224                         if (file.exists()) {
 225                             return file.getAbsolutePath();
 226                         }
 227                     }
 228                     // Then check the extension directory
 229                     File file = new File(dir, name);
 230                     if (file.exists()) {
 231                         return file.getAbsolutePath();
 232                     }
 233                 }
 234                 prevDir = dir;
 235             }
 236             return null;
 237         }
 238 
 239         private static AccessControlContext getContext(File[] dirs)
 240             throws IOException
 241         {
 242             PathPermissions perms =
 243                 new PathPermissions(dirs);
 244 
 245             ProtectionDomain domain = new ProtectionDomain(
 246                 new CodeSource(perms.getCodeBase(),
 247                     (java.security.cert.Certificate[]) null),
 248                 perms);
 249 
 250             AccessControlContext acc =
 251                 new AccessControlContext(new ProtectionDomain[] { domain });
 252 
 253             return acc;
 254         }
 255     }
 256 
 257     /**
 258      * The class loader used for loading from java.class.path.
 259      * runs in a restricted security context.
 260      */
 261     static class AppClassLoader extends URLClassLoader {
 262 
 263         static {
 264             ClassLoader.registerAsParallelCapable();
 265         }
 266 
 267         public static ClassLoader getAppClassLoader(final ClassLoader extcl)
 268             throws IOException
 269         {
 270             final String s = System.getProperty("java.class.path");
 271             final File[] path = (s == null) ? new File[0] : getClassPath(s);
 272 
 273             // Note: on bugid 4256530
 274             // Prior implementations of this doPrivileged() block supplied
 275             // a rather restrictive ACC via a call to the private method
 276             // AppClassLoader.getContext(). This proved overly restrictive
 277             // when loading  classes. Specifically it prevent
 278             // accessClassInPackage.sun.* grants from being honored.
 279             //
 280             return AccessController.doPrivileged(
 281                 new PrivilegedAction<AppClassLoader>() {
 282                     public AppClassLoader run() {
 283                     URL[] urls =
 284                         (s == null) ? new URL[0] : pathToURLs(path);
 285                     return new AppClassLoader(urls, extcl);
 286                 }
 287             });
 288         }
 289 
 290         final URLClassPath ucp;
 291 
 292         /*
 293          * Creates a new AppClassLoader
 294          */
 295         AppClassLoader(URL[] urls, ClassLoader parent) {
 296             super(urls, parent, factory);
 297             ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
 298             ucp.initLookupCache(this);
 299         }
 300 
 301         /**
 302          * Override loadClass so we can checkPackageAccess.
 303          */
 304         public Class<?> loadClass(String name, boolean resolve)
 305             throws ClassNotFoundException
 306         {
 307             int i = name.lastIndexOf('.');
 308             if (i != -1) {
 309                 SecurityManager sm = System.getSecurityManager();
 310                 if (sm != null) {
 311                     sm.checkPackageAccess(name.substring(0, i));
 312                 }
 313             }
 314 
 315             if (ucp.knownToNotExist(name)) {
 316                 // The class of the given name is not found in the parent
 317                 // class loader as well as its local URLClassPath.
 318                 // Check if this class has already been defined dynamically;
 319                 // if so, return the loaded class; otherwise, skip the parent
 320                 // delegation and findClass.
 321                 synchronized (getClassLoadingLock(name)) {
 322                     Class<?> c = findLoadedClass(name);
 323                     if (c != null) {
 324                         return c;
 325                     }
 326                 }
 327                 throw new ClassNotFoundException(name);
 328             }
 329 
 330             return (super.loadClass(name, resolve));
 331         }
 332 
 333         /**
 334          * allow any classes loaded from classpath to exit the VM.
 335          */
 336         protected PermissionCollection getPermissions(CodeSource codesource)
 337         {
 338             PermissionCollection perms = super.getPermissions(codesource);
 339             perms.add(new RuntimePermission("exitVM"));
 340             return perms;
 341         }
 342 
 343         /**
 344          * This class loader supports dynamic additions to the class path
 345          * at runtime.
 346          *
 347          * @see java.lang.instrument.Instrumentation#appendToSystemClassPathSearch
 348          */
 349         private void appendToClassPathForInstrumentation(String path) {
 350             assert(Thread.holdsLock(this));
 351 
 352             // addURL is a no-op if path already contains the URL
 353             super.addURL( getFileURL(new File(path)) );
 354         }
 355 
 356         /**
 357          * create a context that can read any directories (recursively)
 358          * mentioned in the class path. In the case of a jar, it has to
 359          * be the directory containing the jar, not just the jar, as jar
 360          * files might refer to other jar files.
 361          */
 362 
 363         private static AccessControlContext getContext(File[] cp)
 364             throws java.net.MalformedURLException
 365         {
 366             PathPermissions perms =
 367                 new PathPermissions(cp);
 368 
 369             ProtectionDomain domain =
 370                 new ProtectionDomain(new CodeSource(perms.getCodeBase(),
 371                     (java.security.cert.Certificate[]) null),
 372                 perms);
 373 
 374             AccessControlContext acc =
 375                 new AccessControlContext(new ProtectionDomain[] { domain });
 376 
 377             return acc;
 378         }
 379     }
 380 
 381     private static class BootClassPathHolder {
 382         static final URLClassPath bcp;
 383         static {
 384             URL[] urls;
 385             if (bootClassPath != null) {
 386                 urls = AccessController.doPrivileged(
 387                     new PrivilegedAction<URL[]>() {
 388                         public URL[] run() {
 389                             File[] classPath = getClassPath(bootClassPath);
 390                             int len = classPath.length;
 391                             Set<File> seenDirs = new HashSet<File>();
 392                             for (int i = 0; i < len; i++) {
 393                                 File curEntry = classPath[i];
 394                                 // Negative test used to properly handle
 395                                 // nonexistent jars on boot class path
 396                                 if (!curEntry.isDirectory()) {
 397                                     curEntry = curEntry.getParentFile();
 398                                 }
 399                                 if (curEntry != null && seenDirs.add(curEntry)) {
 400                                     MetaIndex.registerDirectory(curEntry);
 401                                 }
 402                             }
 403                             return pathToURLs(classPath);
 404                         }
 405                     }
 406                 );
 407             } else {
 408                 urls = new URL[0];
 409             }
 410             bcp = new URLClassPath(urls, factory);
 411             bcp.initLookupCache(null);
 412         }
 413     }
 414 
 415     public static URLClassPath getBootstrapClassPath() {
 416         return BootClassPathHolder.bcp;
 417     }
 418 
 419     private static URL[] pathToURLs(File[] path) {
 420         URL[] urls = new URL[path.length];
 421         for (int i = 0; i < path.length; i++) {
 422             urls[i] = getFileURL(path[i]);
 423         }
 424         // DEBUG
 425         //for (int i = 0; i < urls.length; i++) {
 426         //  System.out.println("urls[" + i + "] = " + '"' + urls[i] + '"');
 427         //}
 428         return urls;
 429     }
 430 
 431     private static File[] getClassPath(String cp) {
 432         File[] path;
 433         if (cp != null) {
 434             int count = 0, maxCount = 1;
 435             int pos = 0, lastPos = 0;
 436             // Count the number of separators first
 437             while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) {
 438                 maxCount++;
 439                 lastPos = pos + 1;
 440             }
 441             path = new File[maxCount];
 442             lastPos = pos = 0;
 443             // Now scan for each path component
 444             while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) {
 445                 if (pos - lastPos > 0) {
 446                     path[count++] = new File(cp.substring(lastPos, pos));
 447                 } else {
 448                     // empty path component translates to "."
 449                     path[count++] = new File(".");
 450                 }
 451                 lastPos = pos + 1;
 452             }
 453             // Make sure we include the last path component
 454             if (lastPos < cp.length()) {
 455                 path[count++] = new File(cp.substring(lastPos));
 456             } else {
 457                 path[count++] = new File(".");
 458             }
 459             // Trim array to correct size
 460             if (count != maxCount) {
 461                 File[] tmp = new File[count];
 462                 System.arraycopy(path, 0, tmp, 0, count);
 463                 path = tmp;
 464             }
 465         } else {
 466             path = new File[0];
 467         }
 468         // DEBUG
 469         //for (int i = 0; i < path.length; i++) {
 470         //  System.out.println("path[" + i + "] = " + '"' + path[i] + '"');
 471         //}
 472         return path;
 473     }
 474 
 475     private static URLStreamHandler fileHandler;
 476 
 477     static URL getFileURL(File file) {
 478         try {
 479             file = file.getCanonicalFile();
 480         } catch (IOException e) {}
 481 
 482         try {
 483             return ParseUtil.fileToEncodedURL(file);
 484         } catch (MalformedURLException e) {
 485             // Should never happen since we specify the protocol...
 486             throw new InternalError(e);
 487         }
 488     }
 489 
 490     /*
 491      * The stream handler factory for loading system protocol handlers.
 492      */
 493     private static class Factory implements URLStreamHandlerFactory {
 494         private static String PREFIX = "sun.net.www.protocol";
 495 
 496         public URLStreamHandler createURLStreamHandler(String protocol) {
 497             String name = PREFIX + "." + protocol + ".Handler";
 498             try {
 499                 Class<?> c = Class.forName(name);
 500                 return (URLStreamHandler)c.newInstance();
 501             } catch (ReflectiveOperationException e) {
 502                 throw new InternalError("could not load " + protocol +
 503                                         "system protocol handler", e);
 504             }
 505         }
 506     }
 507 }
 508 
 509 class PathPermissions extends PermissionCollection {
 510     // use serialVersionUID from JDK 1.2.2 for interoperability
 511     private static final long serialVersionUID = 8133287259134945693L;
 512 
 513     private File path[];
 514     private Permissions perms;
 515 
 516     URL codeBase;
 517 
 518     PathPermissions(File path[])
 519     {
 520         this.path = path;
 521         this.perms = null;
 522         this.codeBase = null;
 523     }
 524 
 525     URL getCodeBase()
 526     {
 527         return codeBase;
 528     }
 529 
 530     public void add(java.security.Permission permission) {
 531         throw new SecurityException("attempt to add a permission");
 532     }
 533 
 534     private synchronized void init()
 535     {
 536         if (perms != null)
 537             return;
 538 
 539         perms = new Permissions();
 540 
 541         // this is needed to be able to create the classloader itself!
 542         perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
 543 
 544         // add permission to read any "java.*" property
 545         perms.add(new java.util.PropertyPermission("java.*",
 546             SecurityConstants.PROPERTY_READ_ACTION));
 547 
 548         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 549             public Void run() {
 550                 for (int i=0; i < path.length; i++) {
 551                     File f = path[i];
 552                     String path;
 553                     try {
 554                         path = f.getCanonicalPath();
 555                     } catch (IOException ioe) {
 556                         path = f.getAbsolutePath();
 557                     }
 558                     if (i == 0) {
 559                         codeBase = Launcher.getFileURL(new File(path));
 560                     }
 561                     if (f.isDirectory()) {
 562                         if (path.endsWith(File.separator)) {
 563                             perms.add(new FilePermission(path+"-",
 564                                 SecurityConstants.FILE_READ_ACTION));
 565                         } else {
 566                             perms.add(new FilePermission(
 567                                 path + File.separator+"-",
 568                                 SecurityConstants.FILE_READ_ACTION));
 569                         }
 570                     } else {
 571                         int endIndex = path.lastIndexOf(File.separatorChar);
 572                         if (endIndex != -1) {
 573                             path = path.substring(0, endIndex+1) + "-";
 574                             perms.add(new FilePermission(path,
 575                                 SecurityConstants.FILE_READ_ACTION));
 576                         } else {
 577                             // XXX?
 578                         }
 579                     }
 580                 }
 581                 return null;
 582             }
 583         });
 584     }
 585 
 586     public boolean implies(java.security.Permission permission) {
 587         if (perms == null)
 588             init();
 589         return perms.implies(permission);
 590     }
 591 
 592     public java.util.Enumeration<Permission> elements() {
 593         if (perms == null)
 594             init();
 595         synchronized (perms) {
 596             return perms.elements();
 597         }
 598     }
 599 
 600     public String toString() {
 601         if (perms == null)
 602             init();
 603         return perms.toString();
 604     }
 605 }