|
31 | 31 | import org.elasticsearch.logging.Logger; |
32 | 32 |
|
33 | 33 | import java.io.File; |
| 34 | +import java.io.IOException; |
34 | 35 | import java.lang.StackWalker.StackFrame; |
35 | 36 | import java.lang.module.ModuleFinder; |
36 | 37 | import java.lang.module.ModuleReference; |
| 38 | +import java.nio.file.Files; |
37 | 39 | import java.nio.file.Path; |
38 | 40 | import java.util.ArrayList; |
39 | | -import java.util.Arrays; |
40 | 41 | import java.util.HashSet; |
41 | 42 | import java.util.List; |
42 | 43 | import java.util.Map; |
@@ -325,31 +326,53 @@ private static boolean isPathOnDefaultFilesystem(Path path) { |
325 | 326 | return true; |
326 | 327 | } |
327 | 328 |
|
328 | | - public void checkFileRead(Class<?> callerClass, Path... paths) { |
329 | | - for (var path : paths) { |
330 | | - if (isPathOnDefaultFilesystem(path) == false) { |
331 | | - return; |
332 | | - } |
333 | | - var requestingClass = requestingClass(callerClass); |
334 | | - if (isTriviallyAllowed(requestingClass)) { |
335 | | - return; |
336 | | - } |
| 329 | + public void checkFileRead(Class<?> callerClass, Path path) { |
| 330 | + checkFileRead(callerClass, path, false); |
| 331 | + } |
337 | 332 |
|
338 | | - ModuleEntitlements entitlements = getEntitlements(requestingClass); |
339 | | - if (entitlements.fileAccess().canRead(path) == false) { |
340 | | - logger.info(entitlements.fileAccess().toDebugString()); |
341 | | - notEntitled( |
342 | | - Strings.format( |
343 | | - "Not entitled: component [%s], module [%s], class [%s], entitlement [file], operation [read], path [%s]", |
344 | | - entitlements.componentName(), |
345 | | - requestingClass.getModule().getName(), |
346 | | - requestingClass, |
347 | | - Arrays.stream(paths).map(FileAccessTree::normalizePath).collect(Collectors.joining(", ")) |
348 | | - ), |
349 | | - callerClass |
350 | | - ); |
| 333 | + public void checkFileRead(Class<?> callerClass, Path path, boolean followLinks) { |
| 334 | + if (isPathOnDefaultFilesystem(path) == false) { |
| 335 | + return; |
| 336 | + } |
| 337 | + var requestingClass = requestingClass(callerClass); |
| 338 | + if (isTriviallyAllowed(requestingClass)) { |
| 339 | + return; |
| 340 | + } |
| 341 | + |
| 342 | + ModuleEntitlements entitlements = getEntitlements(requestingClass); |
| 343 | + |
| 344 | + Path realPath = null, symbolicLinkPath = null; |
| 345 | + boolean canRead = entitlements.fileAccess().canRead(path); |
| 346 | + |
| 347 | + if (canRead && followLinks) { |
| 348 | + try { |
| 349 | + realPath = path.toRealPath(); |
| 350 | + } catch (IOException e) {} |
| 351 | + try { |
| 352 | + symbolicLinkPath = Files.readSymbolicLink(path); |
| 353 | + canRead = entitlements.fileAccess.canRead(symbolicLinkPath); |
| 354 | + } catch (IOException e) {} |
| 355 | + |
| 356 | + if (canRead == false) { |
| 357 | + logger.warn("Cannot read symbolic link [{} -> {}]: [real path: {}]", path, symbolicLinkPath, realPath); |
351 | 358 | } |
352 | 359 | } |
| 360 | + |
| 361 | + if (canRead == false) { |
| 362 | + logger.info(entitlements.fileAccess().toDebugString()); |
| 363 | + notEntitled( |
| 364 | + Strings.format( |
| 365 | + "Not entitled: component [%s], module [%s], class [%s], entitlement [file], operation [read], path [%s]", |
| 366 | + entitlements.componentName(), |
| 367 | + requestingClass.getModule().getName(), |
| 368 | + requestingClass, |
| 369 | + symbolicLinkPath == null |
| 370 | + ? FileAccessTree.normalizePath(path) |
| 371 | + : FileAccessTree.normalizePath(path) + " -> " + FileAccessTree.normalizePath(symbolicLinkPath) |
| 372 | + ), |
| 373 | + callerClass |
| 374 | + ); |
| 375 | + } |
353 | 376 | } |
354 | 377 |
|
355 | 378 | @SuppressForbidden(reason = "Explicitly checking File apis") |
@@ -612,7 +635,7 @@ Optional<Class<?>> findRequestingClass(Stream<Class<?>> classes) { |
612 | 635 | /** |
613 | 636 | * @return true if permission is granted regardless of the entitlement |
614 | 637 | */ |
615 | | - private static boolean isTriviallyAllowed(Class<?> requestingClass) { |
| 638 | + protected static boolean isTriviallyAllowed(Class<?> requestingClass) { |
616 | 639 | if (logger.isTraceEnabled()) { |
617 | 640 | logger.trace("Stack trace for upcoming trivially-allowed check", new Exception()); |
618 | 641 | } |
|
0 commit comments