mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-16 09:50:30 +00:00
8164705: Remove pathname canonicalization from FilePermission
Reviewed-by: alanb, bpb
This commit is contained in:
parent
0f9a011475
commit
ba9df3533c
@ -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 @@ import sun.security.util.SecurityConstants;
|
||||
* 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 @@ import sun.security.util.SecurityConstants;
|
||||
* <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,33 +157,126 @@ public final class FilePermission extends Permission implements Serializable {
|
||||
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 @@ public final class FilePermission extends Permission implements Serializable {
|
||||
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;
|
||||
this.mask = mask;
|
||||
|
||||
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();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
return cpath;
|
||||
}
|
||||
if (name.equals("<<ALL FILES>>")) {
|
||||
allFiles = true;
|
||||
npath = builtInFS.getPath("");
|
||||
// other fields remain default
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
int len = cpath.length();
|
||||
char last = ((len > 0) ? cpath.charAt(len - 1) : 0);
|
||||
boolean rememberStar = false;
|
||||
if (name.endsWith("*")) {
|
||||
rememberStar = true;
|
||||
recursive = false;
|
||||
name = name.substring(0, name.length()-1) + "-";
|
||||
}
|
||||
|
||||
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);
|
||||
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 {
|
||||
// overkill since they are initialized to false, but
|
||||
// commented out here to remind us...
|
||||
//directory = false;
|
||||
//recursive = false;
|
||||
}
|
||||
if ((cpath = getName()) == null)
|
||||
throw new NullPointerException("name can't be null");
|
||||
|
||||
// XXX: at this point the path should be absolute. die if it isn't?
|
||||
this.mask = mask;
|
||||
|
||||
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();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
return cpath;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// XXX: at this point the path should be absolute. die if it isn't?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -254,7 +405,7 @@ public final class FilePermission extends Permission implements Serializable {
|
||||
* 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 @@ public final class FilePermission extends Permission implements Serializable {
|
||||
*
|
||||
* <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 @@ public final class FilePermission extends Permission implements Serializable {
|
||||
* "/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 @@ public final class FilePermission extends Permission implements Serializable {
|
||||
* @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;
|
||||
}
|
||||
} 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));
|
||||
} 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);
|
||||
}
|
||||
diff = containsPath(this.npath2, that.npath);
|
||||
if (diff >= 1 && recursive) {
|
||||
return true;
|
||||
}
|
||||
if (diff == 1 && directory && !that.directory) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} 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));
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public final class FilePermission extends Permission implements Serializable {
|
||||
* 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 @@ public final class FilePermission extends Permission implements Serializable {
|
||||
|
||||
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 @@ public final class FilePermission extends Permission implements Serializable {
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
if (FilePermCompat.nb) {
|
||||
return Objects.hash(mask, allFiles, directory, recursive, npath);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -27,7 +27,9 @@ package java.security;
|
||||
|
||||
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 @@ public final class AccessControlContext {
|
||||
|
||||
/**
|
||||
* 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 @@ public final class AccessControlContext {
|
||||
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 @@ public final class AccessControlContext {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -32,13 +32,14 @@ import java.lang.ref.WeakReference;
|
||||
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,14 +304,74 @@ public class ProtectionDomain {
|
||||
}
|
||||
|
||||
if (!staticPermissions &&
|
||||
Policy.getPolicyNoCheck().implies(this, perm))
|
||||
Policy.getPolicyNoCheck().implies(this, perm)) {
|
||||
return true;
|
||||
if (permissions != null)
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// called by the VM -- do not remove
|
||||
boolean impliesCreateAccessControlContext() {
|
||||
return implies(SecurityConstants.CREATE_ACC_PERMISSION);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -29,6 +29,7 @@ import java.lang.module.ModuleDescriptor;
|
||||
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 @@ public class SharedSecrets {
|
||||
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 @@ public class SharedSecrets {
|
||||
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);
|
||||
|
||||
@ -41,10 +41,6 @@ import sun.net.www.*;
|
||||
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 @@ public class FileURLConnection extends URLConnection {
|
||||
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;
|
||||
|
||||
@ -45,11 +45,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
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 @@ public class PolicyFile extends java.security.Policy {
|
||||
/**
|
||||
* 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 @@ public class PolicyFile extends java.security.Policy {
|
||||
synchronized (pc) {
|
||||
Enumeration<Permission> e = pc.elements();
|
||||
while (e.hasMoreElements()) {
|
||||
perms.add(e.nextElement());
|
||||
perms.add(FilePermCompat.newPermPlusAltPath(e.nextElement()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1127,7 +1121,7 @@ public class PolicyFile extends java.security.Policy {
|
||||
* 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 @@ public class PolicyFile extends java.security.Policy {
|
||||
* 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 @@ public class PolicyFile extends java.security.Policy {
|
||||
accPs,
|
||||
perms);
|
||||
} else {
|
||||
perms.add(p);
|
||||
perms.add(FilePermCompat.newPermPlusAltPath(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1458,9 +1452,9 @@ public class PolicyFile extends java.security.Policy {
|
||||
}
|
||||
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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
182
jdk/test/java/io/FilePermission/Correctness.java
Normal file
182
jdk/test/java/io/FilePermission/Correctness.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
@ -47,14 +47,14 @@ public class FilePermissionCollection {
|
||||
("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 @@ public class FilePermissionCollection {
|
||||
"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 @@ public class FilePermissionCollection {
|
||||
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 @@ public class FilePermissionCollection {
|
||||
"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 @@ public class FilePermissionCollection {
|
||||
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 @@ public class FilePermissionCollection {
|
||||
"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 @@ public class FilePermissionCollection {
|
||||
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 @@ public class FilePermissionCollection {
|
||||
("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 @@ public class FilePermissionCollection {
|
||||
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 @@ public class FilePermissionCollection {
|
||||
}
|
||||
// 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++;
|
||||
}
|
||||
|
||||
|
||||
82
jdk/test/java/io/FilePermission/ReadFileOnPath.java
Normal file
82
jdk/test/java/io/FilePermission/ReadFileOnPath.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
3
jdk/test/java/io/FilePermission/m/module-info.java
Normal file
3
jdk/test/java/io/FilePermission/m/module-info.java
Normal file
@ -0,0 +1,3 @@
|
||||
module m {
|
||||
exports p;
|
||||
}
|
||||
44
jdk/test/java/io/FilePermission/m/p/App.java
Normal file
44
jdk/test/java/io/FilePermission/m/p/App.java
Normal file
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26,12 +26,12 @@ import java.io.*;
|
||||
|
||||
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");
|
||||
|
||||
@ -39,18 +39,33 @@ POLICY
|
||||
|
||||
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
|
||||
|
||||
@ -29,6 +29,7 @@ import java.nio.file.Files;
|
||||
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.HashMap;
|
||||
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 @@ import java.util.stream.Stream;
|
||||
* .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 @@ import java.util.stream.Stream;
|
||||
*
|
||||
* 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 @@ import java.util.stream.Stream;
|
||||
*
|
||||
* 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 @@ public class Proc {
|
||||
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 @@ public class Proc {
|
||||
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 @@ public class Proc {
|
||||
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 @@ public class Proc {
|
||||
}
|
||||
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 @@ public class Proc {
|
||||
} 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();
|
||||
|
||||
@ -150,9 +150,9 @@ public class CanonPath {
|
||||
//
|
||||
// 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");
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
|
||||
270
jdk/test/sun/security/util/FilePermCompat/CompatImpact.java
Normal file
270
jdk/test/sun/security/util/FilePermCompat/CompatImpact.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
74
jdk/test/sun/security/util/FilePermCompat/Flag.java
Normal file
74
jdk/test/sun/security/util/FilePermCompat/Flag.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user