8164705: Remove pathname canonicalization from FilePermission
Reviewed-by: alanb, bpb
--- a/jdk/src/java.base/share/classes/java/io/FilePermission.java Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/io/FilePermission.java Mon Oct 10 08:28:50 2016 +0800
@@ -25,11 +25,20 @@
package java.io;
+import java.net.URI;
+import java.nio.file.*;
import java.security.*;
import java.util.Enumeration;
+import java.util.Objects;
import java.util.StringJoiner;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
+
+import jdk.internal.misc.JavaIOFilePermissionAccess;
+import jdk.internal.misc.SharedSecrets;
+import sun.nio.fs.DefaultFileSystemProvider;
+import sun.security.action.GetPropertyAction;
+import sun.security.util.FilePermCompat;
import sun.security.util.SecurityConstants;
/**
@@ -41,8 +50,11 @@
* the file separator character, <code>File.separatorChar</code>) indicates
* all the files and directories contained in that directory. A pathname
* that ends with "/-" indicates (recursively) all files
- * and subdirectories contained in that directory. A pathname consisting of
- * the special token "<<ALL FILES>>" matches <b>any</b> file.
+ * and subdirectories contained in that directory. Such a pathname is called
+ * a wildcard pathname. Otherwise, it's a simple pathname.
+ * <P>
+ * A pathname consisting of the special token {@literal "<<ALL FILES>>"}
+ * matches <b>any</b> file.
* <P>
* Note: A pathname consisting of a single "*" indicates all the files
* in the current directory, while a pathname consisting of a single "-"
@@ -75,12 +87,12 @@
* <P>
* Be careful when granting FilePermissions. Think about the implications
* of granting read and especially write access to various files and
- * directories. The "<<ALL FILES>>" permission with write action is
+ * directories. The {@literal "<<ALL FILES>>"} permission with write action is
* especially dangerous. This grants permission to write to the entire
* file system. One thing this effectively allows is replacement of the
* system binary, including the JVM runtime environment.
- *
- * <p>Please note: Code can always read a file from the same
+ * <P>
+ * Please note: Code can always read a file from the same
* directory it's in (or a subdirectory of that directory); it does not
* need explicit permission to do so.
*
@@ -145,34 +157,127 @@
private String actions; // Left null as long as possible, then
// created and re-used in the getAction function.
- // canonicalized dir path. In the case of
- // directories, it is the name "/blah/*" or "/blah/-" without
- // the last character (the "*" or "-").
+ // canonicalized dir path. used by the "old" behavior (nb == false).
+ // In the case of directories, it is the name "/blah/*" or "/blah/-"
+ // without the last character (the "*" or "-").
private transient String cpath;
+ // Following fields used by the "new" behavior (nb == true), in which
+ // input path is not canonicalized. For compatibility (so that granting
+ // FilePermission on "x" allows reading "`pwd`/x", an alternative path
+ // can be added so that both can be used in an implies() check. Please note
+ // the alternative path only deals with absolute/relative path, and does
+ // not deal with symlink/target.
+
+ private transient Path npath; // normalized dir path.
+ private transient Path npath2; // alternative normalized dir path.
+ private transient boolean allFiles; // whether this is <<ALL FILES>>
+
// static Strings used by init(int mask)
private static final char RECURSIVE_CHAR = '-';
private static final char WILD_CHAR = '*';
-/*
- public String toString()
- {
- StringBuffer sb = new StringBuffer();
- sb.append("***\n");
- sb.append("cpath = "+cpath+"\n");
- sb.append("mask = "+mask+"\n");
- sb.append("actions = "+getActions()+"\n");
- sb.append("directory = "+directory+"\n");
- sb.append("recursive = "+recursive+"\n");
- sb.append("***\n");
- return sb.toString();
- }
-*/
+// public String toString() {
+// StringBuffer sb = new StringBuffer();
+// sb.append("*** FilePermission on " + getName() + " ***");
+// for (Field f : FilePermission.class.getDeclaredFields()) {
+// if (!Modifier.isStatic(f.getModifiers())) {
+// try {
+// sb.append(f.getName() + " = " + f.get(this));
+// } catch (Exception e) {
+// sb.append(f.getName() + " = " + e.toString());
+// }
+// sb.append('\n');
+// }
+// }
+// sb.append("***\n");
+// return sb.toString();
+// }
private static final long serialVersionUID = 7930732926638008763L;
/**
+ * Always use the internal default file system, in case it was modified
+ * with java.nio.file.spi.DefaultFileSystemProvider.
+ */
+ private static final java.nio.file.FileSystem builtInFS =
+ DefaultFileSystemProvider.create()
+ .getFileSystem(URI.create("file:///"));
+
+ /**
+ * Creates FilePermission objects with special internals.
+ * See {@link FilePermCompat#newPermPlusAltPath(Permission)} and
+ * {@link FilePermCompat#newPermUsingAltPath(Permission)}.
+ */
+
+ private static final Path here = builtInFS.getPath(
+ GetPropertyAction.privilegedGetProperty("user.dir"));
+
+ /**
+ * A private constructor like a clone, only npath2 is not touched.
+ * @param input
+ */
+ private FilePermission(FilePermission input) {
+ super(input.getName());
+ this.npath = input.npath;
+ this.actions = input.actions;
+ this.allFiles = input.allFiles;
+ this.recursive = input.recursive;
+ this.directory = input.directory;
+ this.cpath = input.cpath;
+ this.mask = input.mask;
+ }
+
+ /**
+ * Returns the alternative path as a Path object, i.e. absolute path
+ * for a relative one, or vice versa.
+ *
+ * @param in a real path w/o "-" or "*" at the end, and not <<ALL FILES>>.
+ * @return the alternative path, or null if cannot find one.
+ */
+ private static Path altPath(Path in) {
+ try {
+ if (!in.isAbsolute()) {
+ return here.resolve(in).normalize();
+ } else {
+ return here.relativize(in).normalize();
+ }
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ static {
+ SharedSecrets.setJavaIOFilePermissionAccess(
+ new JavaIOFilePermissionAccess() {
+ public FilePermission newPermPlusAltPath(FilePermission input) {
+ if (input.npath2 == null && !input.allFiles) {
+ Path npath2 = altPath(input.npath);
+ if (npath2 != null) {
+ FilePermission np = new FilePermission(input);
+ np.npath2 = npath2;
+ return np;
+ }
+ }
+ return input;
+ }
+ public FilePermission newPermUsingAltPath(FilePermission input) {
+ if (!input.allFiles) {
+ Path npath2 = altPath(input.npath);
+ if (npath2 != null) {
+ FilePermission np = new FilePermission(input);
+ np.npath = npath2;
+ return np;
+ }
+ }
+ return null;
+ }
+ }
+ );
+ }
+
+ /**
* initialize a FilePermission object. Common to all constructors.
* Also called during de-serialization.
*
@@ -186,60 +291,106 @@
if (mask == NONE)
throw new IllegalArgumentException("invalid actions mask");
- if ((cpath = getName()) == null)
+ if (FilePermCompat.nb) {
+ String name = getName();
+
+ if (name == null)
+ throw new NullPointerException("name can't be null");
+
+ this.mask = mask;
+
+ if (name.equals("<<ALL FILES>>")) {
+ allFiles = true;
+ npath = builtInFS.getPath("");
+ // other fields remain default
+ return;
+ }
+
+ boolean rememberStar = false;
+ if (name.endsWith("*")) {
+ rememberStar = true;
+ recursive = false;
+ name = name.substring(0, name.length()-1) + "-";
+ }
+
+ try {
+ // new File() can "normalize" some name, for example, "/C:/X" on
+ // Windows. Some JDK codes generate such illegal names.
+ npath = builtInFS.getPath(new File(name).getPath())
+ .normalize();
+ } catch (InvalidPathException ipe) {
+ // Still invalid. For compatibility reason, accept it
+ // but make this permission useless.
+ npath = builtInFS.getPath("-u-s-e-l-e-s-s-");
+ this.mask = NONE;
+ }
+
+ // lastName should always be non-null now
+ Path lastName = npath.getFileName();
+ if (lastName != null && lastName.toString().equals("-")) {
+ directory = true;
+ recursive = !rememberStar;
+ npath = npath.getParent();
+ }
+ if (npath == null) {
+ npath = builtInFS.getPath("");
+ }
+ } else {
+ if ((cpath = getName()) == null)
throw new NullPointerException("name can't be null");
- this.mask = mask;
+ this.mask = mask;
- if (cpath.equals("<<ALL FILES>>")) {
- directory = true;
- recursive = true;
- cpath = "";
- return;
- }
+ if (cpath.equals("<<ALL FILES>>")) {
+ directory = true;
+ recursive = true;
+ cpath = "";
+ return;
+ }
- // store only the canonical cpath if possible
- cpath = AccessController.doPrivileged(new PrivilegedAction<>() {
- public String run() {
- try {
- String path = cpath;
- if (cpath.endsWith("*")) {
- // call getCanonicalPath with a path with wildcard character
- // replaced to avoid calling it with paths that are
- // intended to match all entries in a directory
- path = path.substring(0, path.length()-1) + "-";
- path = new File(path).getCanonicalPath();
- return path.substring(0, path.length()-1) + "*";
- } else {
- return new File(path).getCanonicalPath();
+ // store only the canonical cpath if possible
+ cpath = AccessController.doPrivileged(new PrivilegedAction<>() {
+ public String run() {
+ try {
+ String path = cpath;
+ if (cpath.endsWith("*")) {
+ // call getCanonicalPath with a path with wildcard character
+ // replaced to avoid calling it with paths that are
+ // intended to match all entries in a directory
+ path = path.substring(0, path.length() - 1) + "-";
+ path = new File(path).getCanonicalPath();
+ return path.substring(0, path.length() - 1) + "*";
+ } else {
+ return new File(path).getCanonicalPath();
+ }
+ } catch (IOException ioe) {
+ return cpath;
}
- } catch (IOException ioe) {
- return cpath;
}
- }
- });
+ });
- int len = cpath.length();
- char last = ((len > 0) ? cpath.charAt(len - 1) : 0);
+ int len = cpath.length();
+ char last = ((len > 0) ? cpath.charAt(len - 1) : 0);
- if (last == RECURSIVE_CHAR &&
- cpath.charAt(len - 2) == File.separatorChar) {
- directory = true;
- recursive = true;
- cpath = cpath.substring(0, --len);
- } else if (last == WILD_CHAR &&
- cpath.charAt(len - 2) == File.separatorChar) {
- directory = true;
- //recursive = false;
- cpath = cpath.substring(0, --len);
- } else {
- // overkill since they are initialized to false, but
- // commented out here to remind us...
- //directory = false;
- //recursive = false;
+ if (last == RECURSIVE_CHAR &&
+ cpath.charAt(len - 2) == File.separatorChar) {
+ directory = true;
+ recursive = true;
+ cpath = cpath.substring(0, --len);
+ } else if (last == WILD_CHAR &&
+ cpath.charAt(len - 2) == File.separatorChar) {
+ directory = true;
+ //recursive = false;
+ cpath = cpath.substring(0, --len);
+ } else {
+ // overkill since they are initialized to false, but
+ // commented out here to remind us...
+ //directory = false;
+ //recursive = false;
+ }
+
+ // XXX: at this point the path should be absolute. die if it isn't?
}
-
- // XXX: at this point the path should be absolute. die if it isn't?
}
/**
@@ -254,7 +405,7 @@
* indicates all the files and directories contained in that directory.
* A pathname that ends with "/-" indicates (recursively) all files and
* subdirectories contained in that directory. The special pathname
- * "<<ALL FILES>>" matches any file.
+ * {@literal "<<ALL FILES>>"} matches any file.
*
* <p>A pathname consisting of a single "*" indicates all the files
* in the current directory, while a pathname consisting of a single "-"
@@ -264,6 +415,28 @@
*
* <p>A pathname containing an empty string represents an empty path.
*
+ * @implNote In this implementation, the
+ * {@code jdk.io.permissionsUseCanonicalPath} system property dictates how
+ * the {@code path} argument is processed and stored.
+ * <P>
+ * If the value of the system property is set to {@code true}, {@code path}
+ * is canonicalized and stored as a String object named {@code cpath}.
+ * This means a relative path is converted to an absolute path, a Windows
+ * DOS-style 8.3 path is expanded to a long path, and a symbolic link is
+ * resolved to its target, etc.
+ * <P>
+ * If the value of the system property is set to {@code false}, {@code path}
+ * is converted to a {@link java.nio.file.Path} object named {@code npath}
+ * after {@link Path#normalize() normalization}. No canonicalization is
+ * performed which means the underlying file system is not accessed.
+ * <P>
+ * In either case, the "*" or "-" character at the end of a wildcard
+ * {@code path} is removed before canonicalization or normalization.
+ * It is stored in a separate wildcard flag field.
+ * <P>
+ * The default value of the {@code jdk.io.permissionsUseCanonicalPath}
+ * system property is {@code false} in this implementation.
+ *
* @param path the pathname of the file/directory.
* @param actions the action string.
*
@@ -305,6 +478,38 @@
* "/tmp/*" encompasses all files in the "/tmp" directory,
* including the one named "foo".
* </ul>
+ * <P>
+ * Precisely, a simple pathname implies another simple pathname
+ * if and only if they are equal. A simple pathname never implies
+ * a wildcard pathname. A wildcard pathname implies another wildcard
+ * pathname if and only if all simple pathnames implied by the latter
+ * are implied by the former. A wildcard pathname implies a simple
+ * pathname if and only if
+ * <ul>
+ * <li>if the wildcard flag is "*", the simple pathname's path
+ * must be right inside the wildcard pathname's path.
+ * <li>if the wildcard flag is "-", the simple pathname's path
+ * must be recursively inside the wildcard pathname's path.
+ * </ul>
+ * <P>
+ * {@literal "<<ALL FILES>>"} implies every other pathname. No pathname,
+ * except for {@literal "<<ALL FILES>>"} itself, implies
+ * {@literal "<<ALL FILES>>"}.
+ *
+ * @implNote
+ * If {@code jdk.io.permissionsUseCanonicalPath} is {@code true}, a
+ * simple {@code cpath} is inside a wildcard {@code cpath} if and only if
+ * after removing the base name (the last name in the pathname's name
+ * sequence) from the former the remaining part equals to the latter,
+ * a simple {@code cpath} is recursively inside a wildcard {@code cpath}
+ * if and only if the former starts with the latter.
+ * <p>
+ * If {@code jdk.io.permissionsUseCanonicalPath} is {@code false}, a
+ * simple {@code npath} is inside a wildcard {@code npath} if and only if
+ * {@code simple_npath.relativize(wildcard_npath)} is exactly "..",
+ * a simple {@code npath} is recursively inside a wildcard {@code npath}
+ * if and only if {@code simple_npath.relativize(wildcard_npath)}
+ * is a series of one or more "..".
*
* @param p the permission to check against.
*
@@ -334,45 +539,125 @@
* @return the effective mask
*/
boolean impliesIgnoreMask(FilePermission that) {
- if (this.directory) {
- if (this.recursive) {
- // make sure that.path is longer then path so
- // something like /foo/- does not imply /foo
- if (that.directory) {
- return (that.cpath.length() >= this.cpath.length()) &&
- that.cpath.startsWith(this.cpath);
- } else {
- return ((that.cpath.length() > this.cpath.length()) &&
- that.cpath.startsWith(this.cpath));
+ if (FilePermCompat.nb) {
+ if (allFiles) {
+ return true;
+ }
+ if (that.allFiles) {
+ return false;
+ }
+ // Left at least same level of wildness as right
+ if ((this.recursive && that.recursive) != that.recursive
+ || (this.directory && that.directory) != that.directory) {
+ return false;
+ }
+ // Same npath is good as long as both or neither are directories
+ if (this.npath.equals(that.npath)
+ && this.directory == that.directory) {
+ return true;
+ }
+ int diff = containsPath(this.npath, that.npath);
+ // Right inside left is good if recursive
+ if (diff >= 1 && recursive) {
+ return true;
+ }
+ // Right right inside left if it is element in set
+ if (diff == 1 && directory && !that.directory) {
+ return true;
+ }
+
+ // Hack: if a npath2 field exists, apply the same checks
+ // on it as a fallback.
+ if (this.npath2 != null) {
+ if (this.npath2.equals(that.npath)
+ && this.directory == that.directory) {
+ return true;
+ }
+ diff = containsPath(this.npath2, that.npath);
+ if (diff >= 1 && recursive) {
+ return true;
}
- } else {
- if (that.directory) {
- // if the permission passed in is a directory
- // specification, make sure that a non-recursive
- // permission (i.e., this object) can't imply a recursive
- // permission.
- if (that.recursive)
- return false;
- else
- return (this.cpath.equals(that.cpath));
+ if (diff == 1 && directory && !that.directory) {
+ return true;
+ }
+ }
+
+ return false;
+ } else {
+ if (this.directory) {
+ if (this.recursive) {
+ // make sure that.path is longer then path so
+ // something like /foo/- does not imply /foo
+ if (that.directory) {
+ return (that.cpath.length() >= this.cpath.length()) &&
+ that.cpath.startsWith(this.cpath);
+ } else {
+ return ((that.cpath.length() > this.cpath.length()) &&
+ that.cpath.startsWith(this.cpath));
+ }
} else {
- int last = that.cpath.lastIndexOf(File.separatorChar);
- if (last == -1)
- return false;
- else {
- // this.cpath.equals(that.cpath.substring(0, last+1));
- // Use regionMatches to avoid creating new string
- return (this.cpath.length() == (last + 1)) &&
- this.cpath.regionMatches(0, that.cpath, 0, last+1);
+ if (that.directory) {
+ // if the permission passed in is a directory
+ // specification, make sure that a non-recursive
+ // permission (i.e., this object) can't imply a recursive
+ // permission.
+ if (that.recursive)
+ return false;
+ else
+ return (this.cpath.equals(that.cpath));
+ } else {
+ int last = that.cpath.lastIndexOf(File.separatorChar);
+ if (last == -1)
+ return false;
+ else {
+ // this.cpath.equals(that.cpath.substring(0, last+1));
+ // Use regionMatches to avoid creating new string
+ return (this.cpath.length() == (last + 1)) &&
+ this.cpath.regionMatches(0, that.cpath, 0, last + 1);
+ }
}
}
+ } else if (that.directory) {
+ // if this is NOT recursive/wildcarded,
+ // do not let it imply a recursive/wildcarded permission
+ return false;
+ } else {
+ return (this.cpath.equals(that.cpath));
}
- } else if (that.directory) {
- // if this is NOT recursive/wildcarded,
- // do not let it imply a recursive/wildcarded permission
- return false;
- } else {
- return (this.cpath.equals(that.cpath));
+ }
+ }
+
+ /**
+ * Returns the depth between an outer path p1 and an inner path p2. -1
+ * is returned if
+ *
+ * - p1 does not contains p2.
+ * - this is not decidable. For example, p1="../x", p2="y".
+ * - the depth is not decidable. For example, p1="/", p2="x".
+ *
+ * This method can return 2 if the depth is greater than 2.
+ *
+ * @param p1 the expected outer path, normalized
+ * @param p2 the expected inner path, normalized
+ * @return the depth in between
+ */
+ private static int containsPath(Path p1, Path p2) {
+ Path p;
+ try {
+ p = p2.relativize(p1).normalize();
+ if (p.getName(0).toString().isEmpty()) {
+ return 0;
+ } else {
+ for (Path item: p) {
+ String s = item.toString();
+ if (!s.equals("..")) {
+ return -1;
+ }
+ }
+ return p.getNameCount();
+ }
+ } catch (IllegalArgumentException iae) {
+ return -1;
}
}
@@ -380,6 +665,12 @@
* Checks two FilePermission objects for equality. Checks that <i>obj</i> is
* a FilePermission, and has the same pathname and actions as this object.
*
+ * @implNote More specifically, two pathnames are the same if and only if
+ * they have the same wildcard flag and their {@code cpath}
+ * (if {@code jdk.io.permissionsUseCanonicalPath} is {@code true}) or
+ * {@code npath} (if {@code jdk.io.permissionsUseCanonicalPath}
+ * is {@code false}) are equal. Or they are both {@literal "<<ALL FILES>>"}.
+ *
* @param obj the object we are testing for equality with this object.
* @return <code>true</code> if obj is a FilePermission, and has the same
* pathname and actions as this FilePermission object,
@@ -395,10 +686,18 @@
FilePermission that = (FilePermission) obj;
- return (this.mask == that.mask) &&
- this.cpath.equals(that.cpath) &&
- (this.directory == that.directory) &&
- (this.recursive == that.recursive);
+ if (FilePermCompat.nb) {
+ return (this.mask == that.mask) &&
+ (this.allFiles == that.allFiles) &&
+ this.npath.equals(that.npath) &&
+ (this.directory == that.directory) &&
+ (this.recursive == that.recursive);
+ } else {
+ return (this.mask == that.mask) &&
+ this.cpath.equals(that.cpath) &&
+ (this.directory == that.directory) &&
+ (this.recursive == that.recursive);
+ }
}
/**
@@ -408,7 +707,11 @@
*/
@Override
public int hashCode() {
- return 0;
+ if (FilePermCompat.nb) {
+ return Objects.hash(mask, allFiles, directory, recursive, npath);
+ } else {
+ return 0;
+ }
}
/**
--- a/jdk/src/java.base/share/classes/java/security/AccessControlContext.java Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/security/AccessControlContext.java Mon Oct 10 08:28:50 2016 +0800
@@ -27,7 +27,9 @@
import java.util.ArrayList;
import java.util.List;
+
import sun.security.util.Debug;
+import sun.security.util.FilePermCompat;
import sun.security.util.SecurityConstants;
@@ -175,7 +177,7 @@
/**
* package private to allow calls from ProtectionDomain without performing
- * the security check for {@linkplain SecurityConstants.CREATE_ACC_PERMISSION}
+ * the security check for {@linkplain SecurityConstants#CREATE_ACC_PERMISSION}
* permission
*/
AccessControlContext(AccessControlContext acc,
@@ -253,7 +255,8 @@
if (perms[i].getClass() == AllPermission.class) {
parent = null;
}
- tmp[i] = perms[i];
+ // Add altPath into permission for compatibility.
+ tmp[i] = FilePermCompat.newPermPlusAltPath(perms[i]);
}
}
@@ -443,7 +446,7 @@
}
for (int i=0; i< context.length; i++) {
- if (context[i] != null && !context[i].implies(perm)) {
+ if (context[i] != null && !context[i].impliesWithAltFilePerm(perm)) {
if (dumpDebug) {
debug.println("access denied " + perm);
}
--- a/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java Mon Oct 10 08:28:50 2016 +0800
@@ -32,13 +32,14 @@
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.misc.JavaSecurityAccess;
import jdk.internal.misc.JavaSecurityProtectionDomainAccess;
import static jdk.internal.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache;
import jdk.internal.misc.SharedSecrets;
+import sun.security.provider.PolicyFile;
import sun.security.util.Debug;
+import sun.security.util.FilePermCompat;
import sun.security.util.SecurityConstants;
/**
@@ -303,11 +304,71 @@
}
if (!staticPermissions &&
- Policy.getPolicyNoCheck().implies(this, perm))
+ Policy.getPolicyNoCheck().implies(this, perm)) {
+ return true;
+ }
+ if (permissions != null) {
+ return permissions.implies(perm);
+ }
+
+ return false;
+ }
+
+ /**
+ * This method has the same logic flow as {@link #implies} except that
+ * when the {@link FilePermCompat#compat} flag is on it ensures
+ * FilePermission compatibility after JDK-8164705. {@code implies()}
+ * is called when compat flag is not on or user has extended
+ * {@code ProtectionDomain}.
+ *
+ * This method is called by {@link AccessControlContext#checkPermission}
+ * and not intended to be called by an application.
+ */
+ boolean impliesWithAltFilePerm(Permission perm) {
+
+ // If this is a subclass of ProtectionDomain. Call the old method.
+ if (!FilePermCompat.compat || getClass() != ProtectionDomain.class) {
+ return implies(perm);
+ }
+
+ if (hasAllPerm) {
+ // internal permission collection already has AllPermission -
+ // no need to go to policy
return true;
- if (permissions != null)
- return permissions.implies(perm);
+ }
+
+ Permission p2 = null;
+ boolean p2Calculated = false;
+ if (!staticPermissions) {
+ Policy policy = Policy.getPolicyNoCheck();
+ if (policy instanceof PolicyFile) {
+ // The PolicyFile implementation supports compatibility
+ // inside and it also covers the static permissions.
+ return policy.implies(this, perm);
+ } else {
+ if (policy.implies(this, perm)) {
+ return true;
+ }
+ p2 = FilePermCompat.newPermUsingAltPath(perm);
+ p2Calculated = true;
+ if (p2 != null && policy.implies(this, p2)) {
+ return true;
+ }
+ }
+ }
+ if (permissions != null) {
+ if (permissions.implies(perm)) {
+ return true;
+ } else {
+ if (!p2Calculated) {
+ p2 = FilePermCompat.newPermUsingAltPath(perm);
+ }
+ if (p2 != null) {
+ return permissions.implies(p2);
+ }
+ }
+ }
return false;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaIOFilePermissionAccess.java Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.misc;
+
+import java.io.FilePermission;
+
+public interface JavaIOFilePermissionAccess {
+
+ /**
+ * Returns a new FilePermission plus an alternative path.
+ *
+ * @param input the input
+ * @return the new FilePermission plus the alt path (as npath2)
+ * or the input itself if no alt path is available.
+ */
+ FilePermission newPermPlusAltPath(FilePermission input);
+
+ /**
+ * Returns a new FilePermission using an alternative path.
+ *
+ * @param input the input
+ * @return the new FilePermission using the alt path (as npath)
+ * or null if no alt path is available
+ */
+ FilePermission newPermUsingAltPath(FilePermission input);
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java Mon Oct 10 08:28:50 2016 +0800
@@ -29,6 +29,7 @@
import java.util.jar.JarFile;
import java.io.Console;
import java.io.FileDescriptor;
+import java.io.FilePermission;
import java.io.ObjectInputStream;
import java.io.RandomAccessFile;
import java.security.ProtectionDomain;
@@ -58,6 +59,7 @@
private static JavaNetSocketAccess javaNetSocketAccess;
private static JavaNioAccess javaNioAccess;
private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
+ private static JavaIOFilePermissionAccess javaIOFilePermissionAccess;
private static JavaSecurityProtectionDomainAccess javaSecurityProtectionDomainAccess;
private static JavaSecurityAccess javaSecurityAccess;
private static JavaUtilZipFileAccess javaUtilZipFileAccess;
@@ -201,6 +203,17 @@
javaIOFileDescriptorAccess = jiofda;
}
+ public static JavaIOFilePermissionAccess getJavaIOFilePermissionAccess() {
+ if (javaIOFilePermissionAccess == null)
+ unsafe.ensureClassInitialized(FilePermission.class);
+
+ return javaIOFilePermissionAccess;
+ }
+
+ public static void setJavaIOFilePermissionAccess(JavaIOFilePermissionAccess jiofpa) {
+ javaIOFilePermissionAccess = jiofpa;
+ }
+
public static JavaIOFileDescriptorAccess getJavaIOFileDescriptorAccess() {
if (javaIOFileDescriptorAccess == null)
unsafe.ensureClassInitialized(FileDescriptor.class);
--- a/jdk/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/net/www/protocol/file/FileURLConnection.java Mon Oct 10 08:28:50 2016 +0800
@@ -41,10 +41,6 @@
import java.util.*;
import java.text.SimpleDateFormat;
-import sun.security.action.GetPropertyAction;
-import sun.security.action.GetIntegerAction;
-import sun.security.action.GetBooleanAction;
-
public class FileURLConnection extends URLConnection {
static String CONTENT_LENGTH = "content-length";
@@ -224,8 +220,13 @@
if (File.separatorChar == '/') {
permission = new FilePermission(decodedPath, "read");
} else {
+ // decode could return /c:/x/y/z.
+ if (decodedPath.length() > 2 && decodedPath.charAt(0) == '/'
+ && decodedPath.charAt(2) == ':') {
+ decodedPath = decodedPath.substring(1);
+ }
permission = new FilePermission(
- decodedPath.replace('/',File.separatorChar), "read");
+ decodedPath.replace('/', File.separatorChar), "read");
}
}
return permission;
--- a/jdk/src/java.base/share/classes/sun/security/provider/PolicyFile.java Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/PolicyFile.java Mon Oct 10 08:28:50 2016 +0800
@@ -45,11 +45,7 @@
import jdk.internal.misc.JavaSecurityProtectionDomainAccess;
import static jdk.internal.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache;
import jdk.internal.misc.SharedSecrets;
-import sun.security.util.PolicyUtil;
-import sun.security.util.PropertyExpander;
-import sun.security.util.Debug;
-import sun.security.util.ResourcesMgr;
-import sun.security.util.SecurityConstants;
+import sun.security.util.*;
import sun.net.www.ParseUtil;
/**
@@ -534,8 +530,6 @@
/**
* Reads a policy configuration into the Policy object using a
* Reader object.
- *
- * @param policyFile the policy Reader object.
*/
private boolean init(URL policy, PolicyInfo newInfo, boolean defPolicy) {
@@ -1099,7 +1093,7 @@
synchronized (pc) {
Enumeration<Permission> e = pc.elements();
while (e.hasMoreElements()) {
- perms.add(e.nextElement());
+ perms.add(FilePermCompat.newPermPlusAltPath(e.nextElement()));
}
}
}
@@ -1127,7 +1121,7 @@
* object with additional permissions granted to the specified
* ProtectionDomain.
*
- * @param perm the Permissions to populate
+ * @param perms the Permissions to populate
* @param pd the ProtectionDomain associated with the caller.
*
* @return the set of Permissions according to the policy.
@@ -1157,8 +1151,8 @@
* object with additional permissions granted to the specified
* CodeSource.
*
- * @param permissions the permissions to populate
- * @param codesource the codesource associated with the caller.
+ * @param perms the permissions to populate
+ * @param cs the codesource associated with the caller.
* This encapsulates the original location of the code (where the code
* came from) and the public key(s) of its signer.
*
@@ -1386,7 +1380,7 @@
accPs,
perms);
} else {
- perms.add(p);
+ perms.add(FilePermCompat.newPermPlusAltPath(p));
}
}
}
@@ -1458,9 +1452,9 @@
}
try {
// first try to instantiate the permission
- perms.add(getInstance(sp.getSelfType(),
+ perms.add(FilePermCompat.newPermPlusAltPath(getInstance(sp.getSelfType(),
sb.toString(),
- sp.getSelfActions()));
+ sp.getSelfActions())));
} catch (ClassNotFoundException cnfe) {
// ok, the permission is not in the bootclasspath.
// before we add an UnresolvedPermission, check to see
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/util/FilePermCompat.java Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.util;
+
+import sun.security.action.GetPropertyAction;
+
+import java.io.FilePermission;
+import java.security.Permission;
+import jdk.internal.misc.SharedSecrets;
+
+/**
+ * Take care of FilePermission compatibility after JDK-8164705.
+ */
+public class FilePermCompat {
+ /**
+ * New behavior? Keep compatibility? Both default true.
+ */
+ public static final boolean nb;
+ public static final boolean compat;
+
+ static {
+ String flag = GetPropertyAction.privilegedGetProperty(
+ "jdk.io.permissionsUseCanonicalPath", "false");
+ switch (flag) {
+ case "true":
+ nb = false;
+ compat = false;
+ break;
+ case "false":
+ nb = true;
+ compat = true;
+ break;
+ default:
+ throw new RuntimeException(
+ "Invalid jdk.io.permissionsUseCanonicalPath: " + flag);
+ }
+ }
+
+ public static Permission newPermPlusAltPath(Permission input) {
+ if (compat && input instanceof FilePermission) {
+ return SharedSecrets.getJavaIOFilePermissionAccess()
+ .newPermPlusAltPath((FilePermission) input);
+ }
+ return input;
+ }
+
+ public static Permission newPermUsingAltPath(Permission input) {
+ if (input instanceof FilePermission) {
+ return SharedSecrets.getJavaIOFilePermissionAccess()
+ .newPermUsingAltPath((FilePermission) input);
+ }
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/FilePermission/Correctness.java Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ *
+ * @test
+ * @bug 8164705
+ * @summary Remove pathname canonicalization from FilePermission
+ */
+
+import java.io.FilePermission;
+import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class Correctness {
+
+ static boolean err = false;
+ static Method containsMethod;
+ static boolean isWindows =
+ System.getProperty("os.name").contains("Windows");
+ public static void main(String args[]) throws Exception {
+ check("/", "/");
+ checkNo("/", "/x");
+ checkNo("/", "/../x");
+
+ checkNo("/", "x");
+
+ check("/-", "/*");
+ checkNo("/*", "/-");
+
+ check("/*", "/x");
+ check("/-", "/x");
+ check("/-", "/x/*");
+ check("/-", "/x/-");
+ check("/-", "/x/y");
+ checkNo("/*", "/x/y");
+ check("/x/*", "/x/x");
+ checkNo("/x/-", "/x");
+ checkNo("/x/*", "/x");
+ check("/x/-", "/x/x");
+ check("/x/-", "/x/x/y");
+ checkNo("/x/*", "/x/x/y");
+ checkNo("/x/*", "/x");
+
+ check("*", "x");
+ checkNo("", "x");
+ check("-", "x");
+ check("-", "*");
+ check("-", "a/-");
+ check("-", "a/*");
+ checkNo("*", "a/b");
+ check("a/*", "a/b");
+ check("a/-", "a/*");
+ check("a/-", "a/b/c");
+ checkNo("a/*", "a/b/c");
+
+ check("../", "../");
+ check("../-", "../*");
+ check("../../*", "../../a");
+
+ // If we allow .. and abs/rel checks
+ check("../-", "a");
+ check("../../-", "-");
+ checkNo("../../*", "a");
+ //check("/-", "a");
+ //checkNo("/*", "a");
+ //check("/-", "-");
+
+ try {
+ // containsPath is broken on Windows.
+ containsMethod = FilePermission.class.getDeclaredMethod(
+ "containsPath", Path.class, Path.class);
+ containsMethod.setAccessible(true);
+ System.out.println();
+
+ contains("x", "x", 0);
+ contains("x", "x/y", 1);
+ contains("x", "x/y/z", 2);
+ contains("x", "y", -1);
+ contains("x", "", -1);
+ contains("", "", 0);
+ contains("", "x", 1);
+ contains("", "x/y", 2);
+ contains("/", "/", 0);
+ contains("/", "/x", 1);
+ contains("/", "/x/y", 2);
+ contains("/x", "/x/y", 1);
+ contains("/x", "/y", -1);
+ //contains("/", "..", Integer.MAX_VALUE);
+ //contains("/", "x", Integer.MAX_VALUE);
+ //contains("/", "x/y", Integer.MAX_VALUE);
+ //contains("/", "../x", Integer.MAX_VALUE);
+ contains("/x", "y", -1);
+ contains("x", "/y", -1);
+
+ contains("", "..", -1);
+ contains("", "../x", -1);
+ contains("..", "", 1);
+ contains("..", "x", 2);
+ contains("..", "x/y", 3);
+ contains("../x", "x", -1);
+ contains("../x", "y", -1);
+ contains("../x", "../x/y", 1);
+ contains("../../x", "../../x/y", 1);
+ contains("../../../x", "../../../x/y", 1);
+ contains("../x", "../y", -1);
+ } catch (NoSuchMethodException e) {
+ // Ignored
+ }
+ if (err) throw new Exception("Failed.");
+ }
+
+ // Checks if s2 is inside s1 and depth is expected.
+ static void contains(String s1, String s2, int expected) throws Exception {
+ contains0(s1, s2, expected);
+ if (isWindows) {
+ contains0("C:" + s1, s2, -1);
+ contains0(s1, "C:" + s2, -1);
+ contains0("C:" + s1, "D:" + s2, -1);
+ contains0("C:" + s1, "C:" + s2, expected);
+ }
+ }
+
+ static void contains0(String s1, String s2, int expected) throws Exception {
+ Path p1 = Paths.get(s1);
+ Path p2 = Paths.get(s2);
+ int d = (int)containsMethod.invoke(null, p1, p2);
+ Path p;
+ try {
+ p = p2.relativize(p1);
+ } catch (Exception e) {
+ p = null;
+ }
+ System.out.printf("%-20s -> %-20s: %20s %5d %5d %s\n", s1, s2, p,
+ d, expected, d==expected?"":" WRONG");
+ if (d != expected) {
+ err = true;
+ }
+ }
+
+ static void check(String s1, String s2, boolean expected) {
+ FilePermission fp1 = new FilePermission(s1, "read");
+ FilePermission fp2 = new FilePermission(s2, "read");
+ boolean b = fp1.implies(fp2);
+ System.out.printf("%-30s -> %-30s: %5b %s\n",
+ s1, s2, b, b==expected?"":" WRONG");
+ if (b != expected) {
+ err = true;
+ System.out.println(fp1);
+ System.out.println(fp2);
+ }
+ }
+
+ static void check(String s1, String s2) {
+ check(s1, s2, true);
+ }
+
+ static void checkNo(String s1, String s2) {
+ check(s1, s2, false);
+ }
+}
--- a/jdk/test/java/io/FilePermission/FilePermissionCollection.java Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/test/java/io/FilePermission/FilePermissionCollection.java Mon Oct 10 08:28:50 2016 +0800
@@ -47,14 +47,14 @@
("test 1: add throws IllegalArgExc for wrong perm type");
try {
perms.add(new SecurityPermission("createAccessControlContext"));
- System.err.println("Expected IllegalArgumentException");
+ System.out.println("Expected IllegalArgumentException");
testFail++;
} catch (IllegalArgumentException iae) {}
// test 2
System.out.println("test 2: implies returns false for wrong perm type");
if (perms.implies(new SecurityPermission("getPolicy"))) {
- System.err.println("Expected false, returned true");
+ System.out.println("Expected false, returned true");
testFail++;
}
@@ -63,7 +63,7 @@
"name and action");
perms.add(new FilePermission("/tmp/foo", "read"));
if (!perms.implies(new FilePermission("/tmp/foo", "read"))) {
- System.err.println("Expected true, returned false");
+ System.out.println("Expected true, returned false");
testFail++;
}
@@ -71,7 +71,7 @@
System.out.println("test 4: implies returns false for match on " +
"name but not action");
if (perms.implies(new FilePermission("/tmp/foo", "write"))) {
- System.err.println("Expected false, returned true");
+ System.out.println("Expected false, returned true");
testFail++;
}
@@ -80,7 +80,7 @@
"name and subset of actions");
perms.add(new FilePermission("/tmp/bar", "read, write"));
if (!perms.implies(new FilePermission("/tmp/bar", "write"))) {
- System.err.println("Expected true, returned false");
+ System.out.println("Expected true, returned false");
testFail++;
}
@@ -90,11 +90,11 @@
perms.add(new FilePermission("/tmp/baz", "read"));
perms.add(new FilePermission("/tmp/baz", "write"));
if (!perms.implies(new FilePermission("/tmp/baz", "read"))) {
- System.err.println("Expected true, returned false");
+ System.out.println("Expected true, returned false");
testFail++;
}
if (!perms.implies(new FilePermission("/tmp/baz", "write,read"))) {
- System.err.println("Expected true, returned false");
+ System.out.println("Expected true, returned false");
testFail++;
}
@@ -103,7 +103,7 @@
"and match on action");
perms.add(new FilePermission("/usr/tmp/*", "read"));
if (!perms.implies(new FilePermission("/usr/tmp/foo", "read"))) {
- System.err.println("Expected true, returned false");
+ System.out.println("Expected true, returned false");
testFail++;
}
@@ -111,7 +111,7 @@
System.out.println
("test 8: implies returns false for non-match on wildcard");
if (perms.implies(new FilePermission("/usr/tmp/bar/foo", "read"))) {
- System.err.println("Expected false, returned true");
+ System.out.println("Expected false, returned true");
testFail++;
}
@@ -120,25 +120,25 @@
("test 9: implies returns true for deep wildcard match");
perms.add(new FilePermission("/usr/tmp/-", "read"));
if (!perms.implies(new FilePermission("/usr/tmp/bar/foo", "read"))) {
- System.err.println("Expected true, returned false");
+ System.out.println("Expected true, returned false");
testFail++;
}
// test 10
- System.out.println("test 10: implies returns true for relative match");
+ //System.out.println("test 10: implies returns true for relative match");
perms.add(new FilePermission(".", "read"));
- if (!perms.implies(new FilePermission(System.getProperty("user.dir"),
- "read"))) {
- System.err.println("Expected true, returned false");
- testFail++;
- }
+ //if (!perms.implies(new FilePermission(System.getProperty("user.dir"),
+ // "read"))) {
+ // System.out.println("Expected true, returned false");
+ // testFail++;
+ //}
// test 11
System.out.println("test 11: implies returns true for all " +
"wildcard and match on action");
perms.add(new FilePermission("<<ALL FILES>>", "read"));
if (!perms.implies(new FilePermission("/tmp/foobar", "read"))) {
- System.err.println("Expected true, returned false");
+ System.out.println("Expected true, returned false");
testFail++;
}
@@ -146,7 +146,7 @@
System.out.println("test 12: implies returns false for wildcard " +
"and non-match on action");
if (perms.implies(new FilePermission("/tmp/foobar", "write"))) {
- System.err.println("Expected false, returned true");
+ System.out.println("Expected false, returned true");
testFail++;
}
@@ -160,7 +160,7 @@
}
// the two "/tmp/baz" entries were combined into one
if (numPerms != 7) {
- System.err.println("Expected 7, got " + numPerms);
+ System.out.println("Expected 7, got " + numPerms);
testFail++;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/FilePermission/ReadFileOnPath.java Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8164705
+ * @library /lib/testlibrary /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @run main ReadFileOnPath
+ * @summary Still able to read file on the same path
+ */
+
+import jdk.test.lib.process.ProcessTools;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ReadFileOnPath {
+
+ private static final Path SRC_DIR = Paths.get(System.getProperty("test.src"));
+ private static final Path HERE_DIR = Paths.get(".");
+ private static final Path MODS_DIR = Paths.get("modules");
+
+ public static void main(String args[]) throws Exception {
+ CompilerUtils.compile(SRC_DIR.resolve("m"), MODS_DIR.resolve("m"));
+ Files.write(MODS_DIR.resolve("m/base"), "base".getBytes());
+ Files.write(MODS_DIR.resolve("m/p/child"), "child".getBytes());
+ JarUtils.createJarFile(HERE_DIR.resolve("old.jar"),
+ MODS_DIR.resolve("m"),
+ "base", "p/App.class", "p/child");
+ JarUtils.createJarFile(HERE_DIR.resolve("new.jar"),
+ MODS_DIR.resolve("m"),
+ "module-info.class", "base", "p/App.class", "p/child");
+
+ // exploded module
+ test("--module-path", "modules", "-m", "m/p.App", "SS+++++");
+
+ // module in jar
+ test("--module-path", "new.jar", "-m", "m/p.App", "SSSS++0");
+
+ // exploded classpath
+ test("-cp", "modules/m", "p.App", "SS+++++");
+
+ // classpath in jar
+ test("-cp", "old.jar", "p.App", "SSSS++0");
+ }
+
+ static void test(String... args) throws Exception {
+ List<String> cmds = new ArrayList<>();
+ cmds.add("-Djava.security.manager");
+ cmds.addAll(Arrays.asList(args));
+ cmds.addAll(List.of(
+ "x", "modules/m", "modules/m/base", "modules/m/p/child",
+ "-", "child", "/base", "../base"));
+ ProcessTools.executeTestJvm(cmds.toArray(new String[cmds.size()]))
+ .shouldHaveExitValue(0);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/FilePermission/m/module-info.java Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,3 @@
+module m {
+ exports p;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/FilePermission/m/p/App.java Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,44 @@
+package p;
+import java.io.InputStream;
+import java.io.FileInputStream;
+public class App {
+ public static void main(String[] args) throws Exception {
+ boolean f = true;
+ StringBuilder sb = new StringBuilder();
+ String expected = null;
+ for (String s: args) {
+ if (expected == null) {
+ expected = s;
+ } else if (s.equals("-")) {
+ f = false;
+ } else if (f) {
+ try (InputStream is = new FileInputStream(s)) {
+ is.readAllBytes();
+ sb.append('+');
+ } catch (SecurityException se) {
+ System.out.println(se);
+ sb.append('S');
+ } catch (Exception e) {
+ System.out.println(e);
+ sb.append('-');
+ }
+ } else {
+ try (InputStream is = App.class.getResourceAsStream(s)) {
+ is.readAllBytes();
+ sb.append('+');
+ } catch (NullPointerException npe) {
+ System.out.println(npe);
+ sb.append('0');
+ } catch (Exception e) {
+ System.out.println(e);
+ sb.append('-');
+ }
+ }
+ }
+ if (!sb.toString().equals(expected)) {
+ throw new Exception("Expected " + expected + ", actually " + sb);
+ } else {
+ System.out.println("OK");
+ }
+ }
+}
--- a/jdk/test/java/net/URLClassLoader/getresourceasstream/Test.java Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/test/java/net/URLClassLoader/getresourceasstream/Test.java Mon Oct 10 08:28:50 2016 +0800
@@ -26,12 +26,12 @@
public class Test {
public static void main (String[] args) throws Exception {
- test1();
+ test1(args[0]);
}
- public static void test1 () throws Exception {
+ public static void test1 (String s) throws Exception {
URLClassLoader cl = new URLClassLoader (new URL[] {
- new URL ("file:./test.jar")
+ new URL ("file:" + s)
});
Class clazz = Class.forName ("Test\u00a3", true, cl);
InputStream is = clazz.getResourceAsStream ("Test\u00a3.class");
--- a/jdk/test/java/net/URLClassLoader/getresourceasstream/test.sh Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/test/java/net/URLClassLoader/getresourceasstream/test.sh Mon Oct 10 08:28:50 2016 +0800
@@ -39,18 +39,33 @@
checkExit () {
if [ $? != 0 ]; then
- exit 1;
+ exit $1;
fi
}
${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}/Test.java
cp ${TESTSRC}/test.jar .
-${TESTJAVA}/bin/java ${TESTVMOPTS} Test
-checkExit
+${TESTJAVA}/bin/java ${TESTVMOPTS} Test ./test.jar
+checkExit 1
# try with security manager
-${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.security.policy=file:./policy -Djava.security.manager Test
-checkExit
+${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.security.policy=file:./policy \
+ -Djava.security.manager Test ./test.jar
+checkExit 2
+
+mkdir tmp
+cd tmp
+${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.security.policy=file:../policy \
+ -cp .. -Djava.security.manager Test ../test.jar
+checkExit 3
+
+cd ..
+THISDIR=$(basename $(pwd))
+cd ..
+${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.security.policy=file:$THISDIR/policy \
+ -cp $THISDIR -Djava.security.manager Test $THISDIR/test.jar
+checkExit 4
+
exit 0
--- a/jdk/test/java/security/testlibrary/Proc.java Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/test/java/security/testlibrary/Proc.java Mon Oct 10 08:28:50 2016 +0800
@@ -29,6 +29,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.Permission;
+import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
@@ -37,6 +38,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@@ -49,7 +51,8 @@
* .args("x") // with args
* .env("env", "value") // and an environment variable
* .prop("key","value") // and a system property
- * .perm(perm) // with granted permissions
+ * .grant(file) // grant codes in this codebase
+ * .perm(perm) // with the permission
* .start(); // and start
*
* create/start must be called, args/env/prop/perm can be called zero or
@@ -57,7 +60,7 @@
*
* The controller can call inheritIO to share its I/O to the process.
* Otherwise, it can send data into a proc's stdin with write/println, and
- * read its stdout with readLine. stderr is always redirected to DFILE
+ * read its stdout with readLine. stderr is always redirected to a file
* unless nodump() is called. A protocol is designed to make
* data exchange among the controller and the processes super easy, in which
* useful data are always printed with a special prefix ("PROCISFUN:").
@@ -84,10 +87,10 @@
*
* As the Proc objects are hidden so deeply, two static methods, d(String) and
* d(Throwable) are provided to output info into stderr, where they will
- * normally be appended messages to DFILE (unless nodump() is called).
+ * normally be appended messages to a debug file (unless nodump() is called).
* Developers can view the messages in real time by calling
*
- * tail -f proc.debug
+ * {@code tail -f stderr.<debug>}
*
* TODO:
*
@@ -104,19 +107,24 @@
private BufferedReader br; // the stdout of a process
private String launcher; // Optional: the java program
- private List<Permission> perms = new ArrayList<>();
private List<String> args = new ArrayList<>();
private Map<String,String> env = new HashMap<>();
private Map<String,String> prop = new HashMap();
private boolean inheritIO = false;
private boolean noDump = false;
+ private List<String> cp; // user-provided classpath
private String clazz; // Class to launch
private String debug; // debug flag, controller will show data
- // transfer between procs
+ // transfer between procs. If debug is set,
+ // it MUST be different between Procs.
final private static String PREFIX = "PROCISFUN:";
- final private static String DFILE = "proc.debug";
+
+ // policy file
+ final private StringBuilder perms = new StringBuilder();
+ // temporary saving the grant line in a policy file
+ final private StringBuilder grant = new StringBuilder();
// The following methods are called by controllers
@@ -168,10 +176,68 @@
prop.put(a, b);
return this;
}
- // Adds a perm to policy. Can be called multiple times. In order to make it
- // effective, please also call prop("java.security.manager", "").
+ // Sets classpath. If not called, Proc will choose a classpath. If called
+ // with no arg, no classpath will be used. Can be called multiple times.
+ public Proc cp(String... s) {
+ if (cp == null) {
+ cp = new ArrayList<>();
+ }
+ cp.addAll(Arrays.asList(s));
+ return this;
+ }
+ // Adds a permission to policy. Can be called multiple times.
+ // All perm() calls after a series of grant() calls are grouped into
+ // a single grant block. perm() calls before any grant() call are grouped
+ // into a grant block with no restriction.
+ // Please note that in order to make permissions effective, also call
+ // prop("java.security.manager", "").
public Proc perm(Permission p) {
- perms.add(p);
+ if (grant.length() != 0) { // Right after grant(s)
+ if (perms.length() != 0) { // Not first block
+ perms.append("};\n");
+ }
+ perms.append("grant ").append(grant).append(" {\n");
+ grant.setLength(0);
+ } else {
+ if (perms.length() == 0) { // First block w/o restriction
+ perms.append("grant {\n");
+ }
+ }
+ if (p.getActions().isEmpty()) {
+ String s = String.format("%s \"%s\"",
+ p.getClass().getCanonicalName(),
+ p.getName()
+ .replace("\\", "\\\\").replace("\"", "\\\""));
+ perms.append(" permission ").append(s).append(";\n");
+ } else {
+ String s = String.format("%s \"%s\", \"%s\"",
+ p.getClass().getCanonicalName(),
+ p.getName()
+ .replace("\\", "\\\\").replace("\"", "\\\""),
+ p.getActions());
+ perms.append(" permission ").append(s).append(";\n");
+ }
+ return this;
+ }
+
+ // Adds a grant option to policy. If called in a row, a single grant block
+ // with all options will be created. If there are perm() call(s) between
+ // grant() calls, they belong to different grant blocks
+
+ // grant on a principal
+ public Proc grant(Principal p) {
+ grant.append("principal ").append(p.getClass().getName())
+ .append(" \"").append(p.getName()).append("\", ");
+ return this;
+ }
+ // grant on a codebase
+ public Proc grant(File f) {
+ grant.append("codebase \"").append(f.toURI()).append("\", ");
+ return this;
+ }
+ // arbitrary grant
+ public Proc grant(String v) {
+ grant.append(v).append(", ");
return this;
}
// Starts the proc
@@ -191,30 +257,22 @@
Collections.addAll(cmd, splitProperty("test.vm.opts"));
Collections.addAll(cmd, splitProperty("test.java.opts"));
- cmd.add("-cp");
- cmd.add(System.getProperty("test.class.path") + File.pathSeparator +
- System.getProperty("test.src.path"));
+ if (cp == null) {
+ cmd.add("-cp");
+ cmd.add(System.getProperty("test.class.path") + File.pathSeparator +
+ System.getProperty("test.src.path"));
+ } else if (!cp.isEmpty()) {
+ cmd.add("-cp");
+ cmd.add(cp.stream().collect(Collectors.joining(File.pathSeparator)));
+ }
for (Entry<String,String> e: prop.entrySet()) {
cmd.add("-D" + e.getKey() + "=" + e.getValue());
}
- if (!perms.isEmpty()) {
- Path p = Files.createTempFile(
- Paths.get(".").toAbsolutePath(), "policy", null);
- StringBuilder sb = new StringBuilder();
- sb.append("grant {\n");
- for (Permission perm: perms) {
- // Sometimes a permission has no name or actions.
- // but it's safe to use an empty string.
- String s = String.format("%s \"%s\", \"%s\"",
- perm.getClass().getCanonicalName(),
- perm.getName()
- .replace("\\", "\\\\").replace("\"", "\\\""),
- perm.getActions());
- sb.append(" permission ").append(s).append(";\n");
- }
- sb.append("};\n");
- Files.write(p, sb.toString().getBytes());
+ if (perms.length() > 0) {
+ Path p = Paths.get(getId("policy")).toAbsolutePath();
+ perms.append("};\n");
+ Files.write(p, perms.toString().getBytes());
cmd.add("-Djava.security.policy=" + p.toString());
}
cmd.add(clazz);
@@ -223,6 +281,15 @@
}
if (debug != null) {
System.out.println("PROC: " + debug + " cmdline: " + cmd);
+ for (String c : cmd) {
+ if (c.indexOf('\\') >= 0 || c.indexOf(' ') > 0) {
+ System.out.print('\'' + c + '\'');
+ } else {
+ System.out.print(c);
+ }
+ System.out.print(' ');
+ }
+ System.out.println();
}
ProcessBuilder pb = new ProcessBuilder(cmd);
for (Entry<String,String> e: env.entrySet()) {
@@ -233,12 +300,17 @@
} else if (noDump) {
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
} else {
- pb.redirectError(ProcessBuilder.Redirect.appendTo(new File(DFILE)));
+ pb.redirectError(ProcessBuilder.Redirect
+ .appendTo(new File(getId("stderr"))));
}
p = pb.start();
br = new BufferedReader(new InputStreamReader(p.getInputStream()));
return this;
}
+ String getId(String prefix) {
+ if (debug != null) return prefix + "." + debug;
+ else return prefix + "." + System.identityHashCode(this);
+ }
// Reads a line from stdout of proc
public String readLine() throws IOException {
String s = br.readLine();
--- a/jdk/test/sun/security/provider/PolicyFile/CanonPath.java Fri Oct 07 16:49:31 2016 -0700
+++ b/jdk/test/sun/security/provider/PolicyFile/CanonPath.java Mon Oct 10 08:28:50 2016 +0800
@@ -150,9 +150,9 @@
//
// on unix, /- implies everything
- if (w.implies(u) || !u.implies(w)) {
- throw new Exception("SLASH/- test failed");
- }
+ //if (w.implies(u) || !u.implies(w)) {
+ // throw new Exception("SLASH/- test failed");
+ //}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/util/FilePermCompat/CompatImpact.java Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8164705
+ * @summary check compatibility after FilePermission change
+ * @library /java/security/testlibrary/
+ * @modules java.base/jdk.internal.misc
+ * @run main CompatImpact prepare
+ * @run main CompatImpact builtin
+ * @run main CompatImpact mine
+ * @run main CompatImpact dopriv
+ */
+
+import java.io.File;
+import java.io.FilePermission;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.AccessController;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.security.SecurityPermission;
+
+public class CompatImpact {
+
+ public static void main(String[] args) throws Exception {
+ switch (args[0]) {
+ // copy class files to future classpath
+ case "prepare":
+ // cp in .
+ String cp = System.getProperty("test.classes");
+ Files.copy(Paths.get(cp, "CompatImpact.class"),
+ Paths.get("CompatImpact.class"));
+ Files.copy(Paths.get(cp, "CompatImpact$MP.class"),
+ Paths.get("CompatImpact$MP.class"));
+ Files.write(Paths.get("f"), new byte[10]);
+ // cp in ./sub
+ Files.createDirectory(Paths.get("sub"));
+ Files.copy(Paths.get(cp, "CompatImpact.class"),
+ Paths.get("sub", "CompatImpact.class"));
+ Files.copy(Paths.get(cp, "CompatImpact$MP.class"),
+ Paths.get("sub", "CompatImpact$MP.class"));
+ Files.write(Paths.get("sub", "f"), new byte[10]);
+ // cp in ./inner
+ Files.createDirectory(Paths.get("inner"));
+ Files.copy(Paths.get(cp, "CompatImpact$DoPrivInner.class"),
+ Paths.get("inner", "CompatImpact$DoPrivInner.class"));
+ break;
+ // run tests with different policy impls
+ case "builtin":
+ case "mine":
+ cp = System.getProperty("test.classes");
+ Proc p;
+ String failed = "";
+ String testcase = "";
+ String cwd = System.getProperty("user.dir");
+
+ // Granting a FilePermission on an absolute path
+ testcase = "PonA";
+ p = p(args[0], cwd + "/f")
+ .args("f", cwd + "/f")
+ .debug(testcase)
+ .start();
+ if (p.waitFor() != 0) {
+ Files.copy(Paths.get("stderr." + testcase), System.out);
+ failed += testcase + " ";
+ }
+
+ // Granting a FilePermission on a relative path
+ testcase = "PonR";
+ p = p(args[0], "f")
+ .args("f", cwd + "/f")
+ .debug(testcase)
+ .start();
+ if (p.waitFor() != 0) {
+ Files.copy(Paths.get("stderr." + testcase), System.out);
+ failed += testcase + " ";
+ }
+
+ // Reading file on classpath, not cwd
+ testcase = "cp";
+ String cprel = Paths.get(cwd).relativize(Paths.get(cp))
+ .normalize().toString();
+ p = p(args[0], "x")
+ .args(cp + "/f", cprel + "/f")
+ .debug(testcase)
+ .start();
+ if (p.waitFor() != 0) {
+ Files.copy(Paths.get("stderr." + testcase), System.out);
+ failed += testcase + " ";
+ }
+
+ // Reading file on classpath, cwd
+ testcase = "cpHere";
+ p = p(args[0], "x")
+ .args(cwd + "/f", "f", "RES")
+ .cp(".") // Must! cancel the old CLASSPATH.
+ .debug(testcase)
+ .start();
+ if (p.waitFor() != 0) {
+ Files.copy(Paths.get("stderr." + testcase), System.out);
+ failed += testcase + " ";
+ }
+
+ // Reading file on classpath, cwd
+ testcase = "cpSub";
+ p = p(args[0], "x")
+ .args(cwd + "/sub/f", "sub/f", "RES")
+ .cp("sub") // Must! There's CLASSPATH.
+ .debug(testcase)
+ .start();
+ if (p.waitFor() != 0) {
+ Files.copy(Paths.get("stderr." + testcase), System.out);
+ failed += testcase + " ";
+ }
+
+ if (!failed.isEmpty()) {
+ throw new Exception(failed + "failed");
+ }
+ break;
+ // test <policy_type> <grant> <read...>
+ case "test":
+ if (args[1].equals("mine")) {
+ Policy.setPolicy(new MP(args[2]));
+ }
+ Exception e = null;
+ for (int i = 3; i < args.length; i++) {
+ try {
+ System.out.println(args[i]);
+ if (args[i].equals("RES")) {
+ CompatImpact.class.getResourceAsStream("f")
+ .close();
+ } else {
+ new File(args[i]).exists();
+ }
+ } catch (Exception e2) {
+ e = e2;
+ e2.printStackTrace(System.out);
+ }
+ }
+ if (e != null) {
+ System.err.println("====================");
+ throw e;
+ }
+ break;
+ // doPrivWithPerm test launcher
+ case "dopriv":
+ cwd = System.getProperty("user.dir");
+ // caller (CompatImpact doprivouter, no permission) in sub,
+ // executor (DoPrivInner, AllPermission) in inner.
+ p = Proc.create("CompatImpact")
+ .args("doprivouter")
+ .prop("java.security.manager", "")
+ .grant(new File("inner"))
+ .perm(new AllPermission())
+ .cp("sub", "inner")
+ .debug("doPriv")
+ .args(cwd)
+ .start();
+ if (p.waitFor() != 0) {
+ throw new Exception("dopriv test fails");
+ }
+ break;
+ // doprivouter <cwd>
+ case "doprivouter":
+ DoPrivInner.main(args);
+ break;
+ default:
+ throw new Exception("unknown " + args[0]);
+ }
+ }
+
+ // Call by CompatImpact doprivouter, with AllPermission
+ public static class DoPrivInner {
+ public static void main(String[] args) throws Exception {
+ AccessController.doPrivileged((PrivilegedAction<Boolean>)
+ () -> new File("x").exists(),
+ null,
+ new FilePermission(args[1] + "/x", "read"));
+ AccessController.doPrivileged((PrivilegedAction<Boolean>)
+ () -> new File(args[1] + "/x").exists(),
+ null,
+ new FilePermission("x", "read"));
+ try {
+ AccessController.doPrivileged((PrivilegedAction<Boolean>)
+ () -> new File("x").exists(),
+ null,
+ new FilePermission("y", "read"));
+ throw new Exception("Should not read");
+ } catch (SecurityException se) {
+ // Expected
+ }
+ }
+ }
+
+ // Return a Proc object for different policy types
+ private static Proc p(String type, String f) throws Exception {
+ Proc p = Proc.create("CompatImpact")
+ .prop("java.security.manager", "");
+ p.args("test", type);
+ switch (type) {
+ case "builtin":
+ // For builtin policy, reading access to f can be
+ // granted as a permission
+ p.perm(new FilePermission(f, "read"));
+ p.args("-");
+ break;
+ case "mine":
+ // For my policy, f is passed into test and new MP(f)
+ // will be set as new policy
+ p.perm(new SecurityPermission("setPolicy"));
+ p.args(f);
+ break;
+ default:
+ throw new Exception("unknown " + type);
+ }
+ return p;
+ }
+
+ // My own Policy impl, with only one granted permission, also not smart
+ // enough to know whether ProtectionDomain grants any permission
+ static class MP extends Policy {
+ final PermissionCollection pc;
+ MP(String f) {
+ FilePermission p = new FilePermission(f, "read");
+ pc = p.newPermissionCollection();
+ pc.add(p);
+ }
+ @Override
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return pc;
+ }
+
+ @Override
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return pc;
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return pc.implies(permission);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/util/FilePermCompat/Flag.java Mon Oct 10 08:28:50 2016 +0800
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8164705
+ * @summary check jdk.filepermission.canonicalize
+ * @library /java/security/testlibrary/
+ * @modules java.base/jdk.internal.misc
+ * @run main/othervm -Djdk.io.permissionsUseCanonicalPath=true Flag truetrue
+ * @run main/othervm -Djdk.io.permissionsUseCanonicalPath=false Flag falsetrue
+ * @run main/othervm Flag falsetrue
+ */
+
+import java.io.File;
+import java.io.FilePermission;
+import java.lang.*;
+import java.security.Permission;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+
+public class Flag {
+ public static void main(String[] args) throws Exception {
+
+ boolean test1;
+ boolean test2;
+
+ String here = System.getProperty("user.dir");
+ File abs = new File(here, "x");
+ FilePermission fp1 = new FilePermission("x", "read");
+ FilePermission fp2 = new FilePermission(abs.toString(), "read");
+ test1 = fp1.equals(fp2);
+
+ Policy pol = new Policy() {
+ @java.lang.Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ return fp1.implies(permission);
+ }
+ };
+
+ Policy.setPolicy(pol);
+ System.setSecurityManager(new SecurityManager());
+ try {
+ System.getSecurityManager().checkPermission(fp2);
+ test2 = true;
+ } catch (SecurityException se) {
+ test2 = false;
+ }
+
+ if (!args[0].equals(test1 + "" + test2)) {
+ throw new Exception("Test failed: " + test1 + test2);
+ }
+ }
+}