|  | @@ -14,6 +14,8 @@ import com.sun.tools.attach.AgentLoadException;
 | 
	
		
			
				|  |  |  import com.sun.tools.attach.AttachNotSupportedException;
 | 
	
		
			
				|  |  |  import com.sun.tools.attach.VirtualMachine;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +import org.elasticsearch.core.CheckedConsumer;
 | 
	
		
			
				|  |  | +import org.elasticsearch.core.CheckedSupplier;
 | 
	
		
			
				|  |  |  import org.elasticsearch.core.SuppressForbidden;
 | 
	
		
			
				|  |  |  import org.elasticsearch.entitlement.initialization.EntitlementInitialization;
 | 
	
		
			
				|  |  |  import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
 | 
	
	
		
			
				|  | @@ -22,8 +24,10 @@ import org.elasticsearch.logging.LogManager;
 | 
	
		
			
				|  |  |  import org.elasticsearch.logging.Logger;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import java.io.IOException;
 | 
	
		
			
				|  |  | +import java.lang.reflect.InvocationTargetException;
 | 
	
		
			
				|  |  |  import java.nio.file.Files;
 | 
	
		
			
				|  |  |  import java.nio.file.Path;
 | 
	
		
			
				|  |  | +import java.nio.file.attribute.FileAttribute;
 | 
	
		
			
				|  |  |  import java.util.Map;
 | 
	
		
			
				|  |  |  import java.util.function.Function;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -144,30 +148,31 @@ public class EntitlementBootstrap {
 | 
	
		
			
				|  |  |       * @throws IllegalStateException if the entitlements system can't prevent an unauthorized action of our choosing
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      private static void selfTest() {
 | 
	
		
			
				|  |  | -        ensureCannotStartProcess();
 | 
	
		
			
				|  |  | -        ensureCanCreateTempFile();
 | 
	
		
			
				|  |  | +        ensureCannotStartProcess(ProcessBuilder::start);
 | 
	
		
			
				|  |  | +        ensureCanCreateTempFile(EntitlementBootstrap::createTempFile);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // Try again with reflection
 | 
	
		
			
				|  |  | +        ensureCannotStartProcess(EntitlementBootstrap::reflectiveStartProcess);
 | 
	
		
			
				|  |  | +        ensureCanCreateTempFile(EntitlementBootstrap::reflectiveCreateTempFile);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    private static void ensureCannotStartProcess() {
 | 
	
		
			
				|  |  | +    private static void ensureCannotStartProcess(CheckedConsumer<ProcessBuilder, ?> startProcess) {
 | 
	
		
			
				|  |  |          try {
 | 
	
		
			
				|  |  |              // The command doesn't matter; it doesn't even need to exist
 | 
	
		
			
				|  |  | -            new ProcessBuilder("").start();
 | 
	
		
			
				|  |  | +            startProcess.accept(new ProcessBuilder(""));
 | 
	
		
			
				|  |  |          } catch (NotEntitledException e) {
 | 
	
		
			
				|  |  |              logger.debug("Success: Entitlement protection correctly prevented process creation");
 | 
	
		
			
				|  |  |              return;
 | 
	
		
			
				|  |  | -        } catch (IOException e) {
 | 
	
		
			
				|  |  | +        } catch (Exception e) {
 | 
	
		
			
				|  |  |              throw new IllegalStateException("Failed entitlement protection self-test", e);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          throw new IllegalStateException("Entitlement protection self-test was incorrectly permitted");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /**
 | 
	
		
			
				|  |  | -     * Originally {@code Security.selfTest}.
 | 
	
		
			
				|  |  | -     */
 | 
	
		
			
				|  |  |      @SuppressForbidden(reason = "accesses jvm default tempdir as a self-test")
 | 
	
		
			
				|  |  | -    private static void ensureCanCreateTempFile() {
 | 
	
		
			
				|  |  | +    private static void ensureCanCreateTempFile(CheckedSupplier<Path, ?> createTempFile) {
 | 
	
		
			
				|  |  |          try {
 | 
	
		
			
				|  |  | -            Path p = Files.createTempFile(null, null);
 | 
	
		
			
				|  |  | +            Path p = createTempFile.get();
 | 
	
		
			
				|  |  |              p.toFile().deleteOnExit();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              // Make an effort to clean up the file immediately; also, deleteOnExit leaves the file if the JVM exits abnormally.
 | 
	
	
		
			
				|  | @@ -184,5 +189,24 @@ public class EntitlementBootstrap {
 | 
	
		
			
				|  |  |          logger.debug("Success: Entitlement protection correctly permitted temp file creation");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    @SuppressForbidden(reason = "accesses jvm default tempdir as a self-test")
 | 
	
		
			
				|  |  | +    private static Path createTempFile() throws Exception {
 | 
	
		
			
				|  |  | +        return Files.createTempFile(null, null);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static void reflectiveStartProcess(ProcessBuilder pb) throws Exception {
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +            var start = ProcessBuilder.class.getMethod("start");
 | 
	
		
			
				|  |  | +            start.invoke(pb);
 | 
	
		
			
				|  |  | +        } catch (InvocationTargetException e) {
 | 
	
		
			
				|  |  | +            throw (Exception) e.getCause();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static Path reflectiveCreateTempFile() throws Exception {
 | 
	
		
			
				|  |  | +        return (Path) Files.class.getMethod("createTempFile", String.class, String.class, FileAttribute[].class)
 | 
	
		
			
				|  |  | +            .invoke(null, null, null, new FileAttribute<?>[0]);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      private static final Logger logger = LogManager.getLogger(EntitlementBootstrap.class);
 | 
	
		
			
				|  |  |  }
 |