set ActiveProcessLimit=1 on windows
This blocks process creation, similar to the approach for BSD.
This commit is contained in:
parent
aa56bfa562
commit
ee827198ff
|
@ -217,4 +217,88 @@ final class JNAKernel32Library {
|
|||
* @return true if the function succeeds.
|
||||
*/
|
||||
native boolean CloseHandle(Pointer handle);
|
||||
|
||||
/**
|
||||
* Creates or opens a new job object
|
||||
*
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms682409%28v=vs.85%29.aspx
|
||||
*
|
||||
* @param jobAttributes security attributes
|
||||
* @param name job name
|
||||
* @return job handle if the function succeeds
|
||||
*/
|
||||
native Pointer CreateJobObjectW(Pointer jobAttributes, String name);
|
||||
|
||||
/**
|
||||
* Associates a process with an existing job
|
||||
*
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms681949%28v=vs.85%29.aspx
|
||||
*
|
||||
* @param job job handle
|
||||
* @param process process handle
|
||||
* @return true if the function succeeds
|
||||
*/
|
||||
native boolean AssignProcessToJobObject(Pointer job, Pointer process);
|
||||
|
||||
/**
|
||||
* Basic limit information for a job object
|
||||
*
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms684147%28v=vs.85%29.aspx
|
||||
*/
|
||||
public static class JOBOBJECT_BASIC_LIMIT_INFORMATION extends Structure implements Structure.ByReference {
|
||||
public long PerProcessUserTimeLimit;
|
||||
public long PerJobUserTimeLimit;
|
||||
public int LimitFlags;
|
||||
public SizeT MinimumWorkingSetSize;
|
||||
public SizeT MaximumWorkingSetSize;
|
||||
public int ActiveProcessLimit;
|
||||
public Pointer Affinity;
|
||||
public int PriorityClass;
|
||||
public int SchedulingClass;
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList(new String[] {
|
||||
"PerProcessUserTimeLimit", "PerJobUserTimeLimit", "LimitFlags", "MinimumWorkingSetSize",
|
||||
"MaximumWorkingSetSize", "ActiveProcessLimit", "Affinity", "PriorityClass", "SchedulingClass"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant for JOBOBJECT_BASIC_LIMIT_INFORMATION in Query/Set InformationJobObject
|
||||
*/
|
||||
static final int JOBOBJECT_BASIC_LIMIT_INFORMATION_CLASS = 2;
|
||||
|
||||
/**
|
||||
* Constant for LimitFlags, indicating a process limit has been set
|
||||
*/
|
||||
static final int JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 8;
|
||||
|
||||
/**
|
||||
* Get job limit and state information
|
||||
*
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms684925%28v=vs.85%29.aspx
|
||||
*
|
||||
* @param job job handle
|
||||
* @param infoClass information class constant
|
||||
* @param info pointer to information structure
|
||||
* @param infoLength size of information structure
|
||||
* @param returnLength length of data written back to structure (or null if not wanted)
|
||||
* @return true if the function succeeds
|
||||
*/
|
||||
native boolean QueryInformationJobObject(Pointer job, int infoClass, Pointer info, int infoLength, Pointer returnLength);
|
||||
|
||||
/**
|
||||
* Set job limit and state information
|
||||
*
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms686216%28v=vs.85%29.aspx
|
||||
*
|
||||
* @param job job handle
|
||||
* @param infoClass information class constant
|
||||
* @param info pointer to information structure
|
||||
* @param infoLength size of information structure
|
||||
* @return true if the function succeeds
|
||||
*/
|
||||
native boolean SetInformationJobObject(Pointer job, int infoClass, Pointer info, int infoLength);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ import java.util.Map;
|
|||
* Installs a limited form of secure computing mode,
|
||||
* to filters system calls to block process execution.
|
||||
* <p>
|
||||
* This is only supported on the Linux, Solaris, FreeBSD, OpenBSD, and Mac OS X operating systems.
|
||||
* This is supported on Linux, Solaris, FreeBSD, OpenBSD, Mac OS X, and Windows.
|
||||
* <p>
|
||||
* On Linux it currently supports amd64 and i386 architectures, requires Linux kernel 3.5 or above, and requires
|
||||
* {@code CONFIG_SECCOMP} and {@code CONFIG_SECCOMP_FILTER} compiled into the kernel.
|
||||
|
@ -80,6 +80,8 @@ import java.util.Map;
|
|||
* <li>{@code process-exec}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* On Windows, process creation is restricted with {@code SetInformationJobObject/ActiveProcessLimit}.
|
||||
* <p>
|
||||
* This is not intended as a sandbox. It is another level of security, mostly intended to annoy
|
||||
* security researchers and make their lives more difficult in achieving "remote execution" exploits.
|
||||
* @see <a href="http://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt">
|
||||
|
@ -329,7 +331,8 @@ final class Seccomp {
|
|||
case 1: break; // already set by caller
|
||||
default:
|
||||
int errno = Native.getLastError();
|
||||
if (errno == ENOSYS) {
|
||||
if (errno == EINVAL) {
|
||||
// friendly error, this will be the typical case for an old kernel
|
||||
throw new UnsupportedOperationException("seccomp unavailable: requires kernel 3.5+ with CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER compiled in");
|
||||
} else {
|
||||
throw new UnsupportedOperationException("prctl(PR_GET_NO_NEW_PRIVS): " + JNACLibrary.strerror(errno));
|
||||
|
@ -561,6 +564,48 @@ final class Seccomp {
|
|||
logger.debug("BSD RLIMIT_NPROC initialization successful");
|
||||
}
|
||||
|
||||
// windows impl via job ActiveProcessLimit
|
||||
|
||||
static void windowsImpl() {
|
||||
if (!Constants.WINDOWS) {
|
||||
throw new IllegalStateException("bug: should not be trying to initialize ActiveProcessLimit for an unsupported OS");
|
||||
}
|
||||
|
||||
JNAKernel32Library lib = JNAKernel32Library.getInstance();
|
||||
|
||||
// create a new Job
|
||||
Pointer job = lib.CreateJobObjectW(null, null);
|
||||
if (job == null) {
|
||||
throw new UnsupportedOperationException("CreateJobObject: " + Native.getLastError());
|
||||
}
|
||||
|
||||
try {
|
||||
// retrieve the current basic limits of the job
|
||||
int clazz = JNAKernel32Library.JOBOBJECT_BASIC_LIMIT_INFORMATION_CLASS;
|
||||
JNAKernel32Library.JOBOBJECT_BASIC_LIMIT_INFORMATION limits = new JNAKernel32Library.JOBOBJECT_BASIC_LIMIT_INFORMATION();
|
||||
limits.write();
|
||||
if (!lib.QueryInformationJobObject(job, clazz, limits.getPointer(), limits.size(), null)) {
|
||||
throw new UnsupportedOperationException("QueryInformationJobObject: " + Native.getLastError());
|
||||
}
|
||||
limits.read();
|
||||
// modify the number of active processes to be 1 (exactly the one process we will add to the job).
|
||||
limits.ActiveProcessLimit = 1;
|
||||
limits.LimitFlags = JNAKernel32Library.JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
|
||||
limits.write();
|
||||
if (!lib.SetInformationJobObject(job, clazz, limits.getPointer(), limits.size())) {
|
||||
throw new UnsupportedOperationException("SetInformationJobObject: " + Native.getLastError());
|
||||
}
|
||||
// assign ourselves to the job
|
||||
if (!lib.AssignProcessToJobObject(job, lib.GetCurrentProcess())) {
|
||||
throw new UnsupportedOperationException("AssignProcessToJobObject: " + Native.getLastError());
|
||||
}
|
||||
} finally {
|
||||
lib.CloseHandle(job);
|
||||
}
|
||||
|
||||
logger.debug("Windows ActiveProcessLimit initialization successful");
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to drop the capability to execute for the process.
|
||||
* <p>
|
||||
|
@ -581,6 +626,9 @@ final class Seccomp {
|
|||
} else if (Constants.FREE_BSD || OPENBSD) {
|
||||
bsdImpl();
|
||||
return 1;
|
||||
} else if (Constants.WINDOWS) {
|
||||
windowsImpl();
|
||||
return 1;
|
||||
} else {
|
||||
throw new UnsupportedOperationException("syscall filtering not supported for OS: '" + Constants.OS_NAME + "'");
|
||||
}
|
||||
|
|
|
@ -19,11 +19,15 @@
|
|||
|
||||
package org.elasticsearch.bootstrap;
|
||||
|
||||
import org.apache.lucene.util.Constants;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
/** Simple tests seccomp filter is working. */
|
||||
public class SeccompTests extends ESTestCase {
|
||||
|
||||
/** command to try to run in tests */
|
||||
static final String EXECUTABLE = Constants.WINDOWS ? "calc" : "ls";
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
@ -44,7 +48,7 @@ public class SeccompTests extends ESTestCase {
|
|||
|
||||
public void testNoExecution() throws Exception {
|
||||
try {
|
||||
Runtime.getRuntime().exec("ls");
|
||||
Runtime.getRuntime().exec(EXECUTABLE);
|
||||
fail("should not have been able to execute!");
|
||||
} catch (Exception expected) {
|
||||
// we can't guarantee how its converted, currently its an IOException, like this:
|
||||
|
@ -70,7 +74,7 @@ public class SeccompTests extends ESTestCase {
|
|||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Runtime.getRuntime().exec("ls");
|
||||
Runtime.getRuntime().exec(EXECUTABLE);
|
||||
fail("should not have been able to execute!");
|
||||
} catch (Exception expected) {
|
||||
// ok
|
||||
|
|
Loading…
Reference in New Issue