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

Print this page

        

*** 35,44 **** --- 35,45 ---- import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.lang.ProcessBuilder.Redirect; import java.security.AccessController; import java.security.PrivilegedAction; + import java.util.StringTokenizer; /* This class is for the exclusive use of ProcessBuilder.start() to * create new processes. * * @author Martin Buchholz
*** 153,162 **** --- 154,166 ---- final String path, final long[] stdHandles, final boolean redirectErrorStream) throws IOException { + String prog = ""; + + try { // Win32 CreateProcess requires cmd[0] to be normalized cmd[0] = new File(cmd[0]).getPath(); StringBuilder cmdbuf = new StringBuilder(80); for (int i = 0; i < cmd.length; i++) {
*** 183,192 **** --- 187,227 ---- cmdbuf.append(s); } } String cmdstr = cmdbuf.toString(); + if (cmdstr.length() > 0 && cmdstr.charAt(0) == '"') { + // quoted string must contain the prog name + int end = cmdstr.indexOf('"', 1); + if (end == -1 || cmdstr.indexOf('"', end+1) != -1) { + // either unterminated string or has embedded string + throw new IllegalArgumentException("Invalid string"); + } + prog = cmdstr.substring(1, end); + } else { + prog = getProgramPath(cmdstr); + } + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + try { + security.checkExec(prog); + } catch (SecurityException e) { + // We support invocation of "foo" or "foo.exe" assuming "foo.exe" exists. + // So, the security check should allow the same latitude. + String lprog = prog.toLowerCase(); + if (lprog.endsWith(".exe")) { + // check without the .exe extension + int len = lprog.length(); + String mprog = prog.substring(0, len-4); + security.checkExec(mprog); + } else { + throw e; + } + } + } + handle = create(cmdstr, envblock, path, stdHandles, redirectErrorStream); java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() {
*** 215,227 **** FileDescriptor stderr_fd = new FileDescriptor(); fdAccess.setHandle(stderr_fd, stdHandles[2]); stderr_stream = new FileInputStream(stderr_fd); } ! return null; }}); } public OutputStream getOutputStream() { return stdin_stream; } public InputStream getInputStream() { --- 250,331 ---- FileDescriptor stderr_fd = new FileDescriptor(); fdAccess.setHandle(stderr_fd, stdHandles[2]); stderr_stream = new FileInputStream(stderr_fd); } ! return null; } ! }); ! } catch (IOException e) { ! throw new IOException ( ! "Cannot run program \"" + prog + "\"" ! + (path == null ? "" : " (in directory \"" + path + "\")") ! + ": " + e.getMessage(), e); } + } + /** + * Use the same algorithm as Windows CreateProcess to determine the pathname to + * the given command. Is complicated by the presence of whitespace in the pathname, + * which makes it hard(er) to distinguish between command line parameters and + * whitespace in the command path. We have to check for the existence of files + * to disambiguate. Note, this means when the target does *not* exist, the path + * remains ambiguous, and security checks in particular, will probably fail + * because the whole command string (including args) will be passed to the security + * check. + * + * C:\A B\C D\E F G + * + * We check in the following order (for files existing) + * 1. C:\A.exe + * 2. C:\A + * 3. C:\A B\C.exe + * 4. C:\A B\C + * 5. C:\A B\C D\E.exe + * 6. C:\A B\C D\E (and so on) + * Any token ending in '\\' can't be a valid filename. So it isn't checked. + * If no file is found, then the entire original string is returned. + */ + static String getProgramPath(final String command) { + return java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction<String>() { + public String run() { + StringBuilder sb = new StringBuilder(); + StringTokenizer tokenizer = new StringTokenizer(command, " \t", true); + while (tokenizer.hasMoreTokens()) { + String t = tokenizer.nextToken(); + sb.append(t); + if (t.equals(" ") || t.equals("\t") || t.endsWith("\\")) { + continue; + } + t = sb.toString(); + if (fileHasNoDot(t)) { + // try appending .exe + String t1 = t + ".exe"; + if (exists(t1)) { + return t1; + } + } + if (exists(t)) { + return t; + } + } + return command; + } + }); + } + + // final component of path has no '.' in it + private static boolean fileHasNoDot(String name) { + int n = name.lastIndexOf('\\') + 1; + return name.length() > n ? name.indexOf('.', n) == -1 : false; + } + + private static boolean exists(String name) { + File f = new File (name); + return f.exists(); + } + public OutputStream getOutputStream() { return stdin_stream; } public InputStream getInputStream() {