EvilSecurityTests.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
  3. * or more contributor license agreements. Licensed under the Elastic License
  4. * 2.0 and the Server Side Public License, v 1; you may not use this file except
  5. * in compliance with, at your election, the Elastic License 2.0 or the Server
  6. * Side Public License, v 1.
  7. */
  8. package org.elasticsearch.bootstrap;
  9. import org.apache.lucene.util.Constants;
  10. import org.elasticsearch.common.settings.Settings;
  11. import org.elasticsearch.core.PathUtils;
  12. import org.elasticsearch.core.SuppressForbidden;
  13. import org.elasticsearch.env.Environment;
  14. import org.elasticsearch.env.TestEnvironment;
  15. import org.elasticsearch.test.ESTestCase;
  16. import java.io.FilePermission;
  17. import java.io.IOException;
  18. import java.nio.file.Files;
  19. import java.nio.file.Path;
  20. import java.security.PermissionCollection;
  21. import java.security.Permissions;
  22. import java.util.Set;
  23. import static org.hamcrest.Matchers.containsString;
  24. import static org.hamcrest.Matchers.hasToString;
  25. @SuppressForbidden(reason = "modifies system properties and attempts to create symbolic links intentionally")
  26. public class EvilSecurityTests extends ESTestCase {
  27. /** test generated permissions */
  28. public void testGeneratedPermissions() throws Exception {
  29. Path path = createTempDir();
  30. // make a fake ES home and ensure we only grant permissions to that.
  31. Path esHome = path.resolve("esHome");
  32. Settings.Builder settingsBuilder = Settings.builder();
  33. settingsBuilder.put(Environment.PATH_HOME_SETTING.getKey(), esHome.toString());
  34. Settings settings = settingsBuilder.build();
  35. Path fakeTmpDir = createTempDir();
  36. String realTmpDir = System.getProperty("java.io.tmpdir");
  37. Permissions permissions;
  38. try {
  39. System.setProperty("java.io.tmpdir", fakeTmpDir.toString());
  40. Environment environment = TestEnvironment.newEnvironment(settings);
  41. permissions = Security.createPermissions(environment);
  42. } finally {
  43. System.setProperty("java.io.tmpdir", realTmpDir);
  44. }
  45. // the fake es home
  46. assertNoPermissions(esHome, permissions);
  47. // its parent
  48. assertNoPermissions(esHome.getParent(), permissions);
  49. // some other sibling
  50. assertNoPermissions(esHome.getParent().resolve("other"), permissions);
  51. // double check we overwrote java.io.tmpdir correctly for the test
  52. assertNoPermissions(PathUtils.get(realTmpDir), permissions);
  53. }
  54. /** test generated permissions for all configured paths */
  55. @SuppressWarnings("deprecation") // needs to check settings for deprecated path
  56. @SuppressForbidden(reason = "to create FilePermission object")
  57. public void testEnvironmentPaths() throws Exception {
  58. Path path = createTempDir();
  59. // make a fake ES home and ensure we only grant permissions to that.
  60. Path esHome = path.resolve("esHome");
  61. Settings.Builder settingsBuilder = Settings.builder();
  62. settingsBuilder.put(Environment.PATH_HOME_SETTING.getKey(), esHome.resolve("home").toString());
  63. settingsBuilder.putList(
  64. Environment.PATH_DATA_SETTING.getKey(),
  65. esHome.resolve("data1").toString(),
  66. esHome.resolve("data2").toString()
  67. );
  68. settingsBuilder.put(Environment.PATH_SHARED_DATA_SETTING.getKey(), esHome.resolve("custom").toString());
  69. settingsBuilder.put(Environment.PATH_LOGS_SETTING.getKey(), esHome.resolve("logs").toString());
  70. Settings settings = settingsBuilder.build();
  71. Path fakeTmpDir = createTempDir();
  72. String realTmpDir = System.getProperty("java.io.tmpdir");
  73. Permissions permissions;
  74. Environment environment;
  75. try {
  76. System.setProperty("java.io.tmpdir", fakeTmpDir.toString());
  77. environment = new Environment(settings, esHome.resolve("conf"));
  78. permissions = Security.createPermissions(environment);
  79. } finally {
  80. System.setProperty("java.io.tmpdir", realTmpDir);
  81. }
  82. // the fake es home
  83. assertNoPermissions(esHome, permissions);
  84. // its parent
  85. assertNoPermissions(esHome.getParent(), permissions);
  86. // some other sibling
  87. assertNoPermissions(esHome.getParent().resolve("other"), permissions);
  88. // double check we overwrote java.io.tmpdir correctly for the test
  89. assertNoPermissions(PathUtils.get(realTmpDir), permissions);
  90. // check that all directories got permissions:
  91. // bin file: ro
  92. assertExactPermissions(new FilePermission(environment.binFile().toString(), "read,readlink"), permissions);
  93. // lib file: ro
  94. assertExactPermissions(new FilePermission(environment.libFile().toString(), "read,readlink"), permissions);
  95. // modules file: ro
  96. assertExactPermissions(new FilePermission(environment.modulesFile().toString(), "read,readlink"), permissions);
  97. // config file: ro
  98. assertExactPermissions(new FilePermission(environment.configFile().toString(), "read,readlink"), permissions);
  99. // plugins: ro
  100. assertExactPermissions(new FilePermission(environment.pluginsFile().toString(), "read,readlink"), permissions);
  101. // data paths: r/w
  102. for (Path dataPath : environment.dataFiles()) {
  103. assertExactPermissions(new FilePermission(dataPath.toString(), "read,readlink,write,delete"), permissions);
  104. }
  105. assertExactPermissions(new FilePermission(environment.sharedDataFile().toString(), "read,readlink,write,delete"), permissions);
  106. // logs: r/w
  107. assertExactPermissions(new FilePermission(environment.logsFile().toString(), "read,readlink,write,delete"), permissions);
  108. // temp dir: r/w
  109. assertExactPermissions(new FilePermission(fakeTmpDir.toString(), "read,readlink,write,delete"), permissions);
  110. }
  111. public void testDuplicateDataPaths() throws IOException {
  112. assumeFalse("https://github.com/elastic/elasticsearch/issues/44558", Constants.WINDOWS);
  113. final Path path = createTempDir();
  114. final Path home = path.resolve("home");
  115. final Path data = path.resolve("data");
  116. final Path duplicate;
  117. if (randomBoolean()) {
  118. duplicate = data;
  119. } else {
  120. duplicate = createTempDir().toAbsolutePath().resolve("link");
  121. Files.createSymbolicLink(duplicate, data);
  122. }
  123. final Settings settings = Settings.builder()
  124. .put(Environment.PATH_HOME_SETTING.getKey(), home.toString())
  125. .putList(Environment.PATH_DATA_SETTING.getKey(), data.toString(), duplicate.toString())
  126. .build();
  127. final Environment environment = TestEnvironment.newEnvironment(settings);
  128. final IllegalStateException e = expectThrows(IllegalStateException.class, () -> Security.createPermissions(environment));
  129. assertThat(e, hasToString(containsString("path [" + duplicate.toRealPath() + "] is duplicated by [" + duplicate + "]")));
  130. }
  131. public void testEnsureSymlink() throws IOException {
  132. Path p = createTempDir();
  133. Path exists = p.resolve("exists");
  134. Files.createDirectory(exists);
  135. // symlink
  136. Path linkExists = p.resolve("linkExists");
  137. try {
  138. Files.createSymbolicLink(linkExists, exists);
  139. } catch (UnsupportedOperationException | IOException e) {
  140. assumeNoException("test requires filesystem that supports symbolic links", e);
  141. } catch (SecurityException e) {
  142. assumeNoException("test cannot create symbolic links with security manager enabled", e);
  143. }
  144. Security.ensureDirectoryExists(linkExists);
  145. Files.createTempFile(linkExists, null, null);
  146. }
  147. public void testEnsureBrokenSymlink() throws IOException {
  148. Path p = createTempDir();
  149. // broken symlink
  150. Path brokenLink = p.resolve("brokenLink");
  151. try {
  152. Files.createSymbolicLink(brokenLink, p.resolve("nonexistent"));
  153. } catch (UnsupportedOperationException | IOException e) {
  154. assumeNoException("test requires filesystem that supports symbolic links", e);
  155. } catch (SecurityException e) {
  156. assumeNoException("test cannot create symbolic links with security manager enabled", e);
  157. }
  158. try {
  159. Security.ensureDirectoryExists(brokenLink);
  160. fail("didn't get expected exception");
  161. } catch (IOException expected) {}
  162. }
  163. /** When a configured dir is a symlink, test that permissions work on link target */
  164. public void testSymlinkPermissions() throws IOException {
  165. // see https://github.com/elastic/elasticsearch/issues/12170
  166. assumeFalse("windows does not automatically grant permission to the target of symlinks", Constants.WINDOWS);
  167. Path dir = createTempDir();
  168. Path target = dir.resolve("target");
  169. Files.createDirectory(target);
  170. // symlink
  171. Path link = dir.resolve("link");
  172. try {
  173. Files.createSymbolicLink(link, target);
  174. } catch (UnsupportedOperationException | IOException e) {
  175. assumeNoException("test requires filesystem that supports symbolic links", e);
  176. } catch (SecurityException e) {
  177. assumeNoException("test cannot create symbolic links with security manager enabled", e);
  178. }
  179. Permissions permissions = new Permissions();
  180. FilePermissionUtils.addDirectoryPath(permissions, "testing", link, "read", false);
  181. assertExactPermissions(new FilePermission(link.toString(), "read"), permissions);
  182. assertExactPermissions(new FilePermission(link.resolve("foo").toString(), "read"), permissions);
  183. assertExactPermissions(new FilePermission(target.toString(), "read"), permissions);
  184. assertExactPermissions(new FilePermission(target.resolve("foo").toString(), "read"), permissions);
  185. }
  186. /**
  187. * checks exact file permissions, meaning those and only those for that path.
  188. */
  189. @SuppressForbidden(reason = "to create FilePermission object")
  190. static void assertExactPermissions(FilePermission expected, PermissionCollection actual) {
  191. String target = expected.getName(); // see javadocs
  192. Set<String> permissionSet = asSet(expected.getActions().split(","));
  193. boolean read = permissionSet.remove("read");
  194. boolean readlink = permissionSet.remove("readlink");
  195. boolean write = permissionSet.remove("write");
  196. boolean delete = permissionSet.remove("delete");
  197. boolean execute = permissionSet.remove("execute");
  198. assertTrue("unrecognized permission: " + permissionSet, permissionSet.isEmpty());
  199. assertEquals(read, actual.implies(new FilePermission(target, "read")));
  200. assertEquals(readlink, actual.implies(new FilePermission(target, "readlink")));
  201. assertEquals(write, actual.implies(new FilePermission(target, "write")));
  202. assertEquals(delete, actual.implies(new FilePermission(target, "delete")));
  203. assertEquals(execute, actual.implies(new FilePermission(target, "execute")));
  204. }
  205. /**
  206. * checks that this path has no permissions
  207. */
  208. @SuppressForbidden(reason = "to create FilePermission object")
  209. static void assertNoPermissions(Path path, PermissionCollection actual) {
  210. String target = path.toString();
  211. assertFalse(actual.implies(new FilePermission(target, "read")));
  212. assertFalse(actual.implies(new FilePermission(target, "readlink")));
  213. assertFalse(actual.implies(new FilePermission(target, "write")));
  214. assertFalse(actual.implies(new FilePermission(target, "delete")));
  215. assertFalse(actual.implies(new FilePermission(target, "execute")));
  216. }
  217. }