-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Fix entitlement checks for relative links #124133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,6 @@ | |
| import java.io.FileDescriptor; | ||
| import java.io.FileFilter; | ||
| import java.io.FilenameFilter; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.io.OutputStream; | ||
| import java.io.PrintStream; | ||
|
|
@@ -74,7 +73,6 @@ | |
| import java.nio.file.FileStore; | ||
| import java.nio.file.FileVisitOption; | ||
| import java.nio.file.FileVisitor; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.LinkOption; | ||
| import java.nio.file.OpenOption; | ||
| import java.nio.file.Path; | ||
|
|
@@ -2050,16 +2048,21 @@ public void checkSelectorProviderInheritedChannel(Class<?> callerClass, Selector | |
| policyManager.checkCreateTempFile(callerClass); | ||
| } | ||
|
|
||
| private static Path resolveLinkTarget(Path path, Path target) { | ||
| var parent = path.getParent(); | ||
| return parent == null ? target : parent.resolve(target); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If target is absolute shouldn't we always return it? ie, should we only resolve relative to the parent when target is relative?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's what parent.resolve(target) does. So didn't seem necessary to handle this case specially, but agreed it could make it easier to understand. Can do next week unless someone can pick this up
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are correct, it should not be strictly needed. I think this could be a follow-up if we feel we need it. |
||
| } | ||
|
|
||
| @Override | ||
| public void check$java_nio_file_Files$$createSymbolicLink(Class<?> callerClass, Path link, Path target, FileAttribute<?>... attrs) { | ||
| policyManager.checkFileRead(callerClass, target); | ||
| policyManager.checkFileWrite(callerClass, link); | ||
| policyManager.checkFileRead(callerClass, resolveLinkTarget(link, target)); | ||
| } | ||
|
|
||
| @Override | ||
| public void check$java_nio_file_Files$$createLink(Class<?> callerClass, Path link, Path existing) { | ||
| policyManager.checkFileRead(callerClass, existing); | ||
| policyManager.checkFileWrite(callerClass, link); | ||
| policyManager.checkFileRead(callerClass, resolveLinkTarget(link, existing)); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -2548,13 +2551,13 @@ public void checkCreateDirectory(Class<?> callerClass, FileSystemProvider that, | |
| @Override | ||
| public void checkCreateSymbolicLink(Class<?> callerClass, FileSystemProvider that, Path link, Path target, FileAttribute<?>... attrs) { | ||
| policyManager.checkFileWrite(callerClass, link); | ||
| policyManager.checkFileRead(callerClass, target); | ||
| policyManager.checkFileRead(callerClass, resolveLinkTarget(link, target)); | ||
| } | ||
|
|
||
| @Override | ||
| public void checkCreateLink(Class<?> callerClass, FileSystemProvider that, Path link, Path existing) { | ||
| policyManager.checkFileWrite(callerClass, link); | ||
| policyManager.checkFileRead(callerClass, existing); | ||
| policyManager.checkFileRead(callerClass, resolveLinkTarget(link, existing)); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -2748,14 +2751,7 @@ public void checkPathToRealPath(Class<?> callerClass, Path that, LinkOption... o | |
| followLinks = false; | ||
| } | ||
| } | ||
| if (followLinks) { | ||
| try { | ||
| policyManager.checkFileRead(callerClass, Files.readSymbolicLink(that)); | ||
| } catch (IOException | UnsupportedOperationException e) { | ||
| // that is not a link, or unrelated IOException or unsupported | ||
| } | ||
| } | ||
| policyManager.checkFileRead(callerClass, that); | ||
| policyManager.checkFileRead(callerClass, that, followLinks); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved the check into the |
||
| } | ||
|
|
||
| @Override | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,7 @@ | |
| import org.elasticsearch.logging.Logger; | ||
|
|
||
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.lang.StackWalker.StackFrame; | ||
| import java.lang.module.ModuleFinder; | ||
| import java.lang.module.ModuleReference; | ||
|
|
@@ -325,6 +326,10 @@ private static boolean isPathOnDefaultFilesystem(Path path) { | |
| } | ||
|
|
||
| public void checkFileRead(Class<?> callerClass, Path path) { | ||
| checkFileRead(callerClass, path, false); | ||
| } | ||
|
|
||
| public void checkFileRead(Class<?> callerClass, Path path, boolean followLinks) { | ||
| if (isPathOnDefaultFilesystem(path) == false) { | ||
| return; | ||
| } | ||
|
|
@@ -334,14 +339,28 @@ public void checkFileRead(Class<?> callerClass, Path path) { | |
| } | ||
|
|
||
| ModuleEntitlements entitlements = getEntitlements(requestingClass); | ||
| if (entitlements.fileAccess().canRead(path) == false) { | ||
|
|
||
| Path realPath = null; | ||
| boolean canRead = entitlements.fileAccess().canRead(path); | ||
| if (canRead && followLinks) { | ||
| try { | ||
| realPath = path.toRealPath(); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See PR description why using |
||
| } catch (IOException e) { | ||
| // target not found or other IO error | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't we set canRead = false in this case?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the ideal case we would throw a FileNotFoundException, as done by toRealPath in this case... In the worst case we leaking the information that a file does not exist in a location the caller shouldn't be able to access. The opposite way the location might be OK, but the link broken... throwing a runtime exception the caller doesn't expect / handle in this case seems problematic as well 😔
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based on the ongoing discussion about tweaking exceptions we throw from instrumentation, I would lean towards throwing FileNotFoundException, but that would require changing the signature of our functions in the chain, right?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll leave this as is and will follow up with a separate PR to throw a |
||
| } | ||
| if (realPath != null && realPath.equals(path) == false) { | ||
| canRead = entitlements.fileAccess().canRead(realPath); | ||
| } | ||
| } | ||
|
|
||
| if (canRead == false) { | ||
| notEntitled( | ||
| Strings.format( | ||
| "Not entitled: component [%s], module [%s], class [%s], entitlement [file], operation [read], path [%s]", | ||
| entitlements.componentName(), | ||
| requestingClass.getModule().getName(), | ||
| requestingClass, | ||
| path | ||
| realPath == null ? path : Strings.format("%s -> %s", path, realPath) | ||
| ), | ||
| callerClass | ||
| ); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
..datais marked as exclusive here, it shouldn't ever be visible when testing K8s like file mounts (mount-0.tmp->..data/mount-0.tmp->..version/mount-0.tmp)