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         }
 166 
 167         private static File[] getExtDirs() {
 168             String s = System.getProperty("java.ext.dirs");
 169             File[] dirs;
 170             if (s != null) {
 171                 StringTokenizer st =
 172                     new StringTokenizer(s, File.pathSeparator);
 173                 int count = st.countTokens();
 174                 dirs = new File[count];
 175                 for (int i = 0; i < count; i++) {
 176                     dirs[i] = new File(st.nextToken());
 177                 }
 178             } else {
 179                 dirs = new File[0];
 180             }
 181             return dirs;
 182         }
 183 
 184         private static URL[] getExtURLs(File[] dirs) throws IOException {
 185             Vector<URL> urls = new Vector<URL>();
 186             for (int i = 0; i < dirs.length; i++) {
 187                 String[] files = dirs[i].list();
 188                 if (files != null) {
 189                     for (int j = 0; j < files.length; j++) {
 190                         if (!files[j].equals("meta-index")) {
 191                             File f = new File(dirs[i], files[j]);
 192                             urls.add(getFileURL(f));
 193                         }
 194                     }
 195                 }
 196             }
 197             URL[] ua = new URL[urls.size()];
 198             urls.copyInto(ua);
 199             return ua;
 200         }
 201 
 202         /*
 203          * Searches the installed extension directories for the specified
 204          * library name. For each extension directory, we first look for
 205          * the native library in the subdirectory whose name is the value
 206          * of the system property <code>os.arch</code>. Failing that, we
 207          * look in the extension directory itself.
 208          */
 209         public String findLibrary(String name) {
 210             final String libname = System.mapLibraryName(name);
 211             URL[] urls = super.getURLs();
 212             File prevDir = null;
 213             for (int i = 0; i < urls.length; i++) {
 214                 // Get the ext directory from the URL
 215                 File dir = new File(urls[i].getPath()).getParentFile();
 216                 if (dir != null && !dir.equals(prevDir)) {
 217                     // Look in architecture-specific subdirectory first
 218                     // Read from the saved system properties to avoid deadlock
 219                     final String arch = VM.getSavedProperty("os.arch");
 220                     String pathname = AccessController.doPrivileged(
 221                         new PrivilegedAction<String>() {
 222                             public String run() {
 223                                 if (arch != null) {
 224                                     File file = new File(new File(dir, arch), libname);
 225                                     if (file.exists()) {
 226                                         return file.getAbsolutePath();
 227                                     }
 228                                 }
 229                                 // Then check the extension directory
 230                                 File file = new File(dir, libname);
 231                                 if (file.exists()) {
 232                                     return file.getAbsolutePath();
 233                                 }
 234                                 return null;
 235                             }
 236                         });
 237                     if (pathname != null) {
 238                         return pathname;
 239                     }
 240                 }
 241                 prevDir = dir;
 242             }
 243             return null;
 244         }
 245 
 246         private static AccessControlContext getContext(File[] dirs)
 247             throws IOException
 248         {
 249             PathPermissions perms =
 250                 new PathPermissions(dirs);
 251 
 252             ProtectionDomain domain = new ProtectionDomain(
 253                 new CodeSource(perms.getCodeBase(),
 254                     (java.security.cert.Certificate[]) null),
 255                 perms);
 256 
 257             AccessControlContext acc =
 258                 new AccessControlContext(new ProtectionDomain[] { domain });
 259 
 260             return acc;
 261         }
 262     }
 263 
 264     /**
 265      * The class loader used for loading from java.class.path.
 266      * runs in a restricted security context.
 267      */
 268     static class AppClassLoader extends URLClassLoader {
 269 
 270         static {
 271             ClassLoader.registerAsParallelCapable();
 272         }
 273 
 274         public static ClassLoader getAppClassLoader(final ClassLoader extcl)
 275             throws IOException
 276         {
 277             final String s = System.getProperty("java.class.path");
 278             final File[] path = (s == null) ? new File[0] : getClassPath(s, true);
 279 
 280             // Note: on bugid 4256530
 281             // Prior implementations of this doPrivileged() block supplied
 282             // a rather restrictive ACC via a call to the private method
 283             // AppClassLoader.getContext(). This proved overly restrictive
 284             // when loading  classes. Specifically it prevent
 285             // accessClassInPackage.sun.* grants from being honored.
 286             //
 287             return AccessController.doPrivileged(
 288                 new PrivilegedAction<AppClassLoader>() {
 289                     public AppClassLoader run() {
 290                     URL[] urls =
 291                         (s == null) ? new URL[0] : pathToURLs(path);
 292                     return new AppClassLoader(urls, extcl);
 293                 }
 294             });
 295         }
 296 
 297         /*
 298          * Creates a new AppClassLoader
 299          */
 300         AppClassLoader(URL[] urls, ClassLoader parent) {
 301             super(urls, parent, factory);
 302         }
 303 
 304         /**
 305          * Override loadClass so we can checkPackageAccess.
 306          */
 307         public Class<?> loadClass(String name, boolean resolve)
 308             throws ClassNotFoundException
 309         {
 310             int i = name.lastIndexOf('.');
 311             if (i != -1) {
 312                 SecurityManager sm = System.getSecurityManager();
 313                 if (sm != null) {
 314                     sm.checkPackageAccess(name.substring(0, i));
 315                 }
 316             }
 317             return (super.loadClass(name, resolve));
 318         }
 319 
 320         /**
 321          * allow any classes loaded from classpath to exit the VM.
 322          */
 323         protected PermissionCollection getPermissions(CodeSource codesource)
 324         {
 325             PermissionCollection perms = super.getPermissions(codesource);
 326             perms.add(new RuntimePermission("exitVM"));
 327             return perms;
 328         }
 329 
 330         /**
 331          * This class loader supports dynamic additions to the class path
 332          * at runtime.
 333          *
 334          * @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch
 335          */
 336         private void appendToClassPathForInstrumentation(String path) {
 337             assert(Thread.holdsLock(this));
 338 
 339             // addURL is a no-op if path already contains the URL
 340             super.addURL( getFileURL(new File(path)) );
 341         }
 342 
 343         /**
 344          * create a context that can read any directories (recursively)
 345          * mentioned in the class path. In the case of a jar, it has to
 346          * be the directory containing the jar, not just the jar, as jar
 347          * files might refer to other jar files.
 348          */
 349 
 350         private static AccessControlContext getContext(File[] cp)
 351             throws java.net.MalformedURLException
 352         {
 353             PathPermissions perms =
 354                 new PathPermissions(cp);
 355 
 356             ProtectionDomain domain =
 357                 new ProtectionDomain(new CodeSource(perms.getCodeBase(),
 358                     (java.security.cert.Certificate[]) null),
 359                 perms);
 360 
 361             AccessControlContext acc =
 362                 new AccessControlContext(new ProtectionDomain[] { domain });
 363 
 364             return acc;
 365         }
 366     }
 367 
 368     private static class BootClassPathHolder {
 369         static final URLClassPath bcp;
 370         static {
 371             URL[] urls;
 372             if (bootClassPath != null) {
 373                 urls = AccessController.doPrivileged(
 374                     new PrivilegedAction<URL[]>() {
 375                         public URL[] run() {
 376                             // Skip empty path in boot class path i.e. not default to use CWD
 377                             File[] classPath = getClassPath(bootClassPath, false);
 378                             int len = classPath.length;
 379                             Set<File> seenDirs = new HashSet<File>();
 380                             for (int i = 0; i < len; i++) {
 381                                 File curEntry = classPath[i];
 382                                 // Negative test used to properly handle
 383                                 // nonexistent jars on boot class path
 384                                 if (!curEntry.isDirectory()) {
 385                                     curEntry = curEntry.getParentFile();
 386                                 }
 387                                 if (curEntry != null && seenDirs.add(curEntry)) {
 388                                     MetaIndex.registerDirectory(curEntry);
 389                                 }
 390                             }
 391                             return pathToURLs(classPath);
 392                         }
 393                     }
 394                 );
 395             } else {
 396                 urls = new URL[0];
 397             }
 398             bcp = new URLClassPath(urls, factory);
 399         }
 400     }
 401 
 402     public static URLClassPath getBootstrapClassPath() {
 403         return BootClassPathHolder.bcp;
 404     }
 405 
 406     private static URL[] pathToURLs(File[] path) {
 407         URL[] urls = new URL[path.length];
 408         for (int i = 0; i < path.length; i++) {
 409             urls[i] = getFileURL(path[i]);
 410         }
 411         // DEBUG
 412         //for (int i = 0; i < urls.length; i++) {
 413         //  System.out.println("urls[" + i + "] = " + '"' + urls[i] + '"');
 414         //}
 415         return urls;
 416     }
 417 
 418     private static File[] getClassPath(String cp, boolean defaultToCwd) {
 419         File[] path;
 420         if (cp != null) {
 421             int count = 0, maxCount = 1;
 422             int pos = 0, lastPos = 0;
 423             // Count the number of separators first
 424             while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) {
 425                 maxCount++;
 426                 lastPos = pos + 1;
 427             }
 428             path = new File[maxCount];
 429             lastPos = pos = 0;
 430             // Now scan for each path component
 431             while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) {
 432                 if (pos > lastPos) {
 433                     path[count++] = new File(cp.substring(lastPos, pos));
 434                 } else if (defaultToCwd) {
 435                     // empty path component translates to "."
 436                     path[count++] = new File(".");
 437                 }
 438                 lastPos = pos + 1;
 439             }
 440             // Make sure we include the last path component
 441             if (lastPos < cp.length()) {
 442                 path[count++] = new File(cp.substring(lastPos));
 443             } else if (defaultToCwd) {
 444                 path[count++] = new File(".");
 445             }
 446             // Trim array to correct size
 447             if (count != maxCount) {
 448                 File[] tmp = new File[count];
 449                 System.arraycopy(path, 0, tmp, 0, count);
 450                 path = tmp;
 451             }
 452         } else {
 453             path = new File[0];
 454         }
 455         // DEBUG
 456         //for (int i = 0; i < path.length; i++) {
 457         //  System.out.println("path[" + i + "] = " + '"' + path[i] + '"');
 458         //}
 459         return path;
 460     }
 461 
 462     private static URLStreamHandler fileHandler;
 463 
 464     static URL getFileURL(File file) {
 465         try {
 466             file = file.getCanonicalFile();
 467         } catch (IOException e) {}
 468 
 469         try {
 470             return ParseUtil.fileToEncodedURL(file);
 471         } catch (MalformedURLException e) {
 472             // Should never happen since we specify the protocol...
 473             throw new InternalError(e);
 474         }
 475     }
 476 
 477     /*
 478      * The stream handler factory for loading system protocol handlers.
 479      */
 480     private static class Factory implements URLStreamHandlerFactory {
 481         private static String PREFIX = "sun.net.www.protocol";
 482 
 483         public URLStreamHandler createURLStreamHandler(String protocol) {
 484             String name = PREFIX + "." + protocol + ".Handler";
 485             try {
 486                 Class<?> c = Class.forName(name);
 487                 return (URLStreamHandler)c.newInstance();
 488             } catch (ReflectiveOperationException e) {
 489                 throw new InternalError("could not load " + protocol +
 490                                         "system protocol handler", e);
 491             }
 492         }
 493     }
 494 }
 495 
 496 class PathPermissions extends PermissionCollection {
 497     // use serialVersionUID from JDK 1.2.2 for interoperability
 498     private static final long serialVersionUID = 8133287259134945693L;
 499 
 500     private File path[];
 501     private Permissions perms;
 502 
 503     URL codeBase;
 504 
 505     PathPermissions(File path[])
 506     {
 507         this.path = path;
 508         this.perms = null;
 509         this.codeBase = null;
 510     }
 511 
 512     URL getCodeBase()
 513     {
 514         return codeBase;
 515     }
 516 
 517     public void add(java.security.Permission permission) {
 518         throw new SecurityException("attempt to add a permission");
 519     }
 520 
 521     private synchronized void init()
 522     {
 523         if (perms != null)
 524             return;
 525 
 526         perms = new Permissions();
 527 
 528         // this is needed to be able to create the classloader itself!
 529         perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
 530 
 531         // add permission to read any "java.*" property
 532         perms.add(new java.util.PropertyPermission("java.*",
 533             SecurityConstants.PROPERTY_READ_ACTION));
 534 
 535         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 536             public Void run() {
 537                 for (int i=0; i < path.length; i++) {
 538                     File f = path[i];
 539                     String path;
 540                     try {
 541                         path = f.getCanonicalPath();
 542                     } catch (IOException ioe) {
 543                         path = f.getAbsolutePath();
 544                     }
 545                     if (i == 0) {
 546                         codeBase = Launcher.getFileURL(new File(path));
 547                     }
 548                     if (f.isDirectory()) {
 549                         if (path.endsWith(File.separator)) {
 550                             perms.add(new FilePermission(path+"-",
 551                                 SecurityConstants.FILE_READ_ACTION));
 552                         } else {
 553                             perms.add(new FilePermission(
 554                                 path + File.separator+"-",
 555                                 SecurityConstants.FILE_READ_ACTION));
 556                         }
 557                     } else {
 558                         int endIndex = path.lastIndexOf(File.separatorChar);
 559                         if (endIndex != -1) {
 560                             path = path.substring(0, endIndex+1) + "-";
 561                             perms.add(new FilePermission(path,
 562                                 SecurityConstants.FILE_READ_ACTION));
 563                         } else {
 564                             // XXX?
 565                         }
 566                     }
 567                 }
 568                 return null;
 569             }
 570         });
 571     }
 572 
 573     public boolean implies(java.security.Permission permission) {
 574         if (perms == null)
 575             init();
 576         return perms.implies(permission);
 577     }
 578 
 579     public java.util.Enumeration<Permission> elements() {
 580         if (perms == null)
 581             init();
 582         synchronized (perms) {
 583             return perms.elements();
 584         }
 585     }
 586 
 587     public String toString() {
 588         if (perms == null)
 589             init();
 590         return perms.toString();
 591     }
 592 }