Merge pull request #15055 from rmuir/fun_with_windows
set ActiveProcessLimit=1 on windows
This commit is contained in:
commit
29853744a3
|
@ -217,4 +217,88 @@ final class JNAKernel32Library {
|
||||||
* @return true if the function succeeds.
|
* @return true if the function succeeds.
|
||||||
*/
|
*/
|
||||||
native boolean CloseHandle(Pointer handle);
|
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,
|
* Installs a limited form of secure computing mode,
|
||||||
* to filters system calls to block process execution.
|
* to filters system calls to block process execution.
|
||||||
* <p>
|
* <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>
|
* <p>
|
||||||
* On Linux it currently supports amd64 and i386 architectures, requires Linux kernel 3.5 or above, and requires
|
* 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.
|
* {@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>
|
* <li>{@code process-exec}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <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
|
* 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.
|
* 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">
|
* @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
|
case 1: break; // already set by caller
|
||||||
default:
|
default:
|
||||||
int errno = Native.getLastError();
|
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");
|
throw new UnsupportedOperationException("seccomp unavailable: requires kernel 3.5+ with CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER compiled in");
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException("prctl(PR_GET_NO_NEW_PRIVS): " + JNACLibrary.strerror(errno));
|
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");
|
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.
|
* Attempt to drop the capability to execute for the process.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -581,6 +626,9 @@ final class Seccomp {
|
||||||
} else if (Constants.FREE_BSD || OPENBSD) {
|
} else if (Constants.FREE_BSD || OPENBSD) {
|
||||||
bsdImpl();
|
bsdImpl();
|
||||||
return 1;
|
return 1;
|
||||||
|
} else if (Constants.WINDOWS) {
|
||||||
|
windowsImpl();
|
||||||
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException("syscall filtering not supported for OS: '" + Constants.OS_NAME + "'");
|
throw new UnsupportedOperationException("syscall filtering not supported for OS: '" + Constants.OS_NAME + "'");
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,15 @@
|
||||||
|
|
||||||
package org.elasticsearch.bootstrap;
|
package org.elasticsearch.bootstrap;
|
||||||
|
|
||||||
|
import org.apache.lucene.util.Constants;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
||||||
/** Simple tests seccomp filter is working. */
|
/** Simple tests seccomp filter is working. */
|
||||||
public class SeccompTests extends ESTestCase {
|
public class SeccompTests extends ESTestCase {
|
||||||
|
|
||||||
|
/** command to try to run in tests */
|
||||||
|
static final String EXECUTABLE = Constants.WINDOWS ? "calc" : "ls";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
|
@ -44,7 +48,7 @@ public class SeccompTests extends ESTestCase {
|
||||||
|
|
||||||
public void testNoExecution() throws Exception {
|
public void testNoExecution() throws Exception {
|
||||||
try {
|
try {
|
||||||
Runtime.getRuntime().exec("ls");
|
Runtime.getRuntime().exec(EXECUTABLE);
|
||||||
fail("should not have been able to execute!");
|
fail("should not have been able to execute!");
|
||||||
} catch (Exception expected) {
|
} catch (Exception expected) {
|
||||||
// we can't guarantee how its converted, currently its an IOException, like this:
|
// we can't guarantee how its converted, currently its an IOException, like this:
|
||||||
|
@ -70,7 +74,7 @@ public class SeccompTests extends ESTestCase {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Runtime.getRuntime().exec("ls");
|
Runtime.getRuntime().exec(EXECUTABLE);
|
||||||
fail("should not have been able to execute!");
|
fail("should not have been able to execute!");
|
||||||
} catch (Exception expected) {
|
} catch (Exception expected) {
|
||||||
// ok
|
// ok
|
||||||
|
|
Loading…
Reference in New Issue