src/windows/classes/java/lang/ProcessImpl.java

Print this page

        

*** 35,44 **** --- 35,46 ---- 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,152 **** --- 145,238 ---- } } } + 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,292 **** return fileToRun.getPath(); } private long handle = 0; private OutputStream stdin_stream; private InputStream stdout_stream; private InputStream stderr_stream; ! private ProcessImpl(final 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); // 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); ! } } - String cmdstr = cmdbuf.toString(); handle = create(cmdstr, envblock, path, stdHandles, redirectErrorStream); java.security.AccessController.doPrivileged( --- 311,395 ---- 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(String cmd[], final String envblock, final String path, final long[] stdHandles, final boolean redirectErrorStream) throws IOException { ! 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. ! cmdstr = createCommandLine( ! // We need the extended verification procedure for CMD files. ! isShellFile(executablePath), ! quoteString(executablePath), ! cmd); } handle = create(cmdstr, envblock, path, stdHandles, redirectErrorStream); java.security.AccessController.doPrivileged(