src/windows/classes/java/lang/ProcessImpl.java
Print this page
@@ -35,10 +35,12 @@
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.lang.ProcessBuilder.Redirect;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.util.LinkedList;
+import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit;
/* This class is for the exclusive use of ProcessBuilder.start() to
* create new processes.
*
@@ -143,10 +145,94 @@
}
}
}
+ private static final String QUOTATION = "\"";
+ private static final String ARGS_SEPARATOR = QUOTATION + " \t\n\r\f";
+
+ /* Parses the command string parameter into the executable name and
+ * program arguments.
+ *
+ * The command string is broken into tokens. The token separator is a space
+ * or quota character. The space inside quotation is not a token separator.
+ * There are no escape sequences.
+ */
+ private static String[] getTokensFromCommand(String command)
+ {
+ StringTokenizer st = new StringTokenizer(command, ARGS_SEPARATOR, true);
+ LinkedList<String> args = new LinkedList<>();
+ StringBuilder sb = null;
+ boolean quotation = false;
+ while(st.hasMoreTokens()) {
+ String token = st.nextToken(quotation
+ ? QUOTATION
+ : ARGS_SEPARATOR);
+ if (QUOTATION.equals(token)) {
+ if (quotation) {
+ args.addLast(sb.append(token).toString());
+ sb = null;
+ } else {
+ sb = new StringBuilder();
+ sb.append(token);
+ }
+ quotation = !quotation;
+ } else {
+ if (quotation) {
+ sb.append(token);
+ } else {
+ token = token.trim();
+ if (!token.isEmpty()) {
+ args.addLast(token);
+ }
+ }
+ }
+ }
+ if (sb != null) {
+ args.addLast(sb.toString());
+ }
+ return args.toArray(new String[args.size()]);
+ }
+
+ private String createCommandLine(boolean isCmdFile,
+ final String executablePath,
+ final String cmd[])
+ {
+ StringBuilder cmdbuf = new StringBuilder(80);
+
+ cmdbuf.append(executablePath);
+
+ for (int i = 1; i < cmd.length; i++) {
+ cmdbuf.append(' ');
+ String s = cmd[i];
+ if (needsEscaping(isCmdFile, s)) {
+ cmdbuf.append('"');
+ cmdbuf.append(s);
+
+ // The code protects the [java.exe] and console command line
+ // parser, that interprets the [\"] combination as an escape
+ // sequence for the ["] char.
+ // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
+ //
+ // If the argument is an FS path, doubling of the tail [\]
+ // char is not a problem for non-console applications.
+ //
+ // The [\"] sequence is not an escape sequence for the [cmd.exe]
+ // command line parser. The case of the [""] tail escape
+ // sequence could not be realized due to the argument validation
+ // procedure.
+ if (!isCmdFile && s.endsWith("\\")) {
+ cmdbuf.append('\\');
+ }
+ cmdbuf.append('"');
+ } else {
+ cmdbuf.append(s);
+ }
+ }
+ return cmdbuf.toString();
+ }
+
// We guarantee the only command file execution for implicit [cmd.exe] run.
// http://technet.microsoft.com/en-us/library/bb490954.aspx
private static final char CMD_BAT_ESCAPE[] = {' ', '\t', '<', '>', '&', '|', '^'};
private static final char WIN32_EXECUTABLE_ESCAPE[] = {' ', '\t', '<', '>'};
@@ -225,68 +311,85 @@
return fileToRun.getPath();
}
+ private boolean isShellFile(String executablePath) {
+ String upPath = executablePath.toUpperCase();
+ return (upPath.endsWith(".CMD") || upPath.endsWith(".BAT"));
+ }
+
+ private String quoteString(String arg) {
+ StringBuilder argbuf = new StringBuilder(arg.length() + 2);
+ return argbuf.append('"').append(arg).append('"').toString();
+ }
+
+
private long handle = 0;
private OutputStream stdin_stream;
private InputStream stdout_stream;
private InputStream stderr_stream;
- private ProcessImpl(final String cmd[],
+ private ProcessImpl(String cmd[],
final String envblock,
final String path,
final long[] stdHandles,
final boolean redirectErrorStream)
throws IOException
{
- // The [executablePath] is not quoted for any case.
- String executablePath = getExecutablePath(cmd[0]);
-
- // We need to extend the argument verification procedure
- // to guarantee the only command file execution for implicit [cmd.exe]
- // run.
- String upPath = executablePath.toUpperCase();
- boolean isCmdFile = (upPath.endsWith(".CMD") || upPath.endsWith(".BAT"));
-
- StringBuilder cmdbuf = new StringBuilder(80);
+ String cmdstr;
+ SecurityManager security = System.getSecurityManager();
+ if (security == null
+ && System.getProperty("jdk.lang.Process.allowAmbigousCommands") != null) {
+ // Legacy mode.
+
+ // Normalize path if possible.
+ String executablePath = new File(cmd[0]).getPath();
+
+ // No worry about internal and unpaired ["] .
+ if (needsEscaping(false, executablePath) ) {
+ executablePath = quoteString(executablePath);
+ }
+ cmdstr = createCommandLine(
+ false, //legacy mode doesn't worry about extended verification
+ executablePath,
+ cmd);
+ } else {
+ String executablePath;
+ try {
+ executablePath = getExecutablePath(cmd[0]);
+ } catch (IllegalArgumentException e) {
+ // Workaround for the calls like
+ // Runtime.getRuntime().exec("\"C:\\Program Files\\foo\" bar")
+
+ // No chance to avoid CMD/BAT injection, except to do the work
+ // right from the beginning. Otherwise we have too many corner
+ // cases from
+ // Runtime.getRuntime().exec(String[] cmd [, ...])
+ // calls with internal ["] and escape sequences.
+
+ // Restore original command line.
+ StringBuilder join = new StringBuilder();
+ for (String s : cmd) {
+ // terminal space in command line is ok
+ join.append(s).append(' ');
+ }
+
+ // Parse the command line again.
+ cmd = getTokensFromCommand(join.toString());
+ executablePath = getExecutablePath(cmd[0]);
+ }
// Quotation protects from interpretation of the [path] argument as
// start of longer path with spaces. Quotation has no influence to
// [.exe] extension heuristic.
- cmdbuf.append('"');
- cmdbuf.append(executablePath);
- cmdbuf.append('"');
-
- for (int i = 1; i < cmd.length; i++) {
- cmdbuf.append(' ');
- String s = cmd[i];
- if (needsEscaping(isCmdFile, s)) {
- cmdbuf.append('"');
- cmdbuf.append(s);
-
- // The code protects the [java.exe] and console command line
- // parser, that interprets the [\"] combination as an escape
- // sequence for the ["] char.
- // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
- //
- // If the argument is an FS path, doubling of the tail [\]
- // char is not a problem for non-console applications.
- //
- // The [\"] sequence is not an escape sequence for the [cmd.exe]
- // command line parser. The case of the [""] tail escape
- // sequence could not be realized due to the argument validation
- // procedure.
- if (!isCmdFile && s.endsWith("\\")) {
- cmdbuf.append('\\');
- }
- cmdbuf.append('"');
- } else {
- cmdbuf.append(s);
- }
+ cmdstr = createCommandLine(
+ // We need the extended verification procedure for CMD files.
+ isShellFile(executablePath),
+ quoteString(executablePath),
+ cmd);
}
- String cmdstr = cmdbuf.toString();
handle = create(cmdstr, envblock, path,
stdHandles, redirectErrorStream);
java.security.AccessController.doPrivileged(