/*
* Copyright 2009-2012 Michael Dalton
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jtaint;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.Random;
import java.util.StringTokenizer;
public class ExecTest
{
private final int maxlen;
private final Random r;
private boolean exception;
private static String currentCommand;
private static String currentOp;
private static String[] currentCommandArray;
private static List currentCommandList;
public ExecTest(int maxlen, Random r) {
this.maxlen = maxlen;
this.r = r;
clearException();
}
private void clearException() { exception = false; }
private void setException() { exception = true; }
private boolean pendingException() { return exception; }
private String randString(int taintP, int slen) {
int len = 1 + r.nextInt(slen);
@StringBuilder@ sb = new @StringBuilder@(len);
BitSet b = new BitSet();
for (int i = 0; i < len; i++) {
int c, clen;
boolean taint = r.nextInt(taintP) == 0;
int oldlen = sb.length();
c = r.nextInt(256);
if (c == '\0') /* Don't prematurely null terminate unless tainted */
taint = true;
sb.append((char) c);
b.set(oldlen, taint);
}
return new String(sb.toString(), new Taint(b, sb.length()));
}
private String randProgram() {
String s, prefix = "/javataint/";
BitSet b = new BitSet();
StringTokenizer st;
for (int i = 0; i < prefix.length(); i++)
if (r.nextInt(64) == 0)
b.set(i);
s = new String(prefix, new Taint(b, prefix.length())) +
randString(64,64);
st = new StringTokenizer(s);
return st.nextToken();
}
private String randCommand() {
int len = 1 + r.nextInt(255);
@StringBuilder@ sb = new @StringBuilder@(len);
String prg = randProgram();
if (prg.@internal@isTainted())
setException();
sb.append(prg);
while (sb.length() < len) {
int wslen = 1 + r.nextInt(7);
for (int i = 0; i < wslen; i++)
sb.append(' ');
sb.append(randString(16,16));
}
return sb.toString();
}
private String[] randStringArray() {
String[] s = new String[1 + r.nextInt(7)];
for (int i = 0; i < s.length; i++)
s[i] = randString(16, 16);
return s;
}
private String[] randCommandArray() {
String[] s = new String[1 + r.nextInt(7)];
for (int i = 0; i < s.length; i++) {
if (i == 0) {
s[i] = randProgram();
if (s[i].@internal@isTainted())
setException();
} else
s[i] = randString(16, 16);
}
return s;
}
private List randCommandList() {
int len = 1 + r.nextInt(7);
List list = new ArrayList();
for (int i = 0; i < len; i++) {
if (i == 0) {
String prg = randProgram();
list.add(prg);
if (prg.@internal@isTainted())
setException();
} else
list.add(randString(16,16));
}
return list;
}
private void testRuntime() throws IOException {
String[] envp = { "x=y", "foobar=baz" };
File cwd = new File(System.getProperty("user.dir"));
envp[1] = StringUtil.toTainted(envp[1]);
switch(r.nextInt(6)) {
case 0: /* exec(String cmd) */
currentCommand = randCommand();
currentOp = "Runtime.exec(String cmd)";
Runtime.getRuntime().exec(currentCommand);
break;
case 1: /* exec(String cmd, String[] envp) */
currentCommand = randCommand();
currentOp = "Runtime.exec(String cmd, String[] envp)";
Runtime.getRuntime().exec(currentCommand, envp);
break;
case 2: /* exec(String cmd, String[] envp, File dir) */
currentCommand = randCommand();
currentOp = "Runtime.exec(String cmd, String[] envp, File dir)";
Runtime.getRuntime().exec(currentCommand, envp, cwd);
break;
case 3: /* exec(String[] cmdArray) */
currentCommandArray = randCommandArray();
currentOp = "Runtime.exec(String cmdArray)";
Runtime.getRuntime().exec(currentCommandArray);
break;
case 4: /* exec(String[] cmdArray, String[] envp) */
currentCommandArray = randCommandArray();
currentOp = "Runtime.exec(String cmdArray, String[] envp)";
Runtime.getRuntime().exec(currentCommandArray, envp);
break;
case 5: /* exec(String[] cmdArray, String[] envp, File dir) */
currentCommandArray = randCommandArray();
currentOp = "Runtime.exec(String cmdArray, String[] envp, "
+ "File dir)";
Runtime.getRuntime().exec(currentCommandArray, envp, cwd);
break;
default:
throw new RuntimeException("switch");
}
}
private void testValid() throws IOException, InterruptedException {
String cmd = randCommand();
cmd = "/bin/echo " + cmd;
String[] cmdArray = randCommandArray();
cmdArray[0] = "/bin/echo";
List cmdList = randCommandList();
cmdList.add(0,"/bin/echo");
clearException();
Runtime.getRuntime().exec(cmd).waitFor();
Runtime.getRuntime().exec(cmdArray).waitFor();
//[ifJava5+]
new ProcessBuilder(cmdArray).start().waitFor();
new ProcessBuilder(cmdList).start().waitFor();
new ProcessBuilder("/foo").command(cmdArray).start().waitFor();
new ProcessBuilder("/foo").command(cmdList).start().waitFor();
//[fiJava5+]
}
//[ifJava5+]
private void testProcessBuilder() throws IOException {
ProcessBuilder pb;
switch(r.nextInt(4)) {
case 0: // ProcessBuilder(String[] cmdArray)
currentCommandArray = randCommandArray();
currentOp = "ProcessBuilder(String[] cmdArray)";
pb = new ProcessBuilder(currentCommandArray);
break;
case 1: // ProcessBuilder(List<String> cmdList)
currentCommandList = randCommandList();
currentOp = "ProcessBuilder(List<String> cmdList)";
pb = new ProcessBuilder(currentCommandList);
break;
case 2: // command(String[] cmdArray)
currentCommandArray = randCommandArray();
currentOp = "ProcessBuilder.command(String[] cmdArray)";
pb = new ProcessBuilder("/bin/ls");
pb.command(currentCommandArray);
break;
case 3: // command(List<String> cmdList)
currentCommandList = randCommandList();
currentOp = "ProcessBuilder.command(List<String> cmdArray)";
pb = new ProcessBuilder("/bin/ls");
pb.command(currentCommandList);
break;
default:
throw new RuntimeException("switch");
}
pb.start();
}
//[fiJava5+]
private void test() throws IOException {
currentCommand = null;
currentCommandArray = null;
currentCommandList = null;
currentOp = null;
//[ifJava4]
testRuntime();
//[fiJava4]
//[ifJava5+]
if (r.nextBoolean())
testRuntime();
else
testProcessBuilder();
//[fiJava5+]
}
public static void main(String[] args) {
long seed = System.currentTimeMillis();
int maxlen = 65536;
int nrtest = 16384;
Random r;
ExecTest et;
String logfile = "ExecTest.log";
PrintStream ps = null;
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-s"))
seed = Long.decode(args[++i]).longValue();
else if (args[i].equals("-l"))
maxlen = Integer.decode(args[++i]).intValue();
else if (args[i].equals("-n"))
nrtest = Integer.decode(args[++i]).intValue();
else if (args[i].equals("-f"))
logfile = args[++i];
else {
System.out.println("Usage: java ExecTest "
+ "[-s randomSeed] "
+ "[-l maximumLengthofCommand] "
+ "[-n NumberofTests]"
+ "[-f logFileName]");
System.exit(-1);
}
}
try {
ps = new PrintStream(new FileOutputStream(logfile));
} catch (FileNotFoundException e) {
System.out.println("Error opening logfile [" + logfile + "]: " + e);
System.exit(-1);
}
ps.print("-s ");
ps.print(seed);
ps.print(" -l ");
ps.print(maxlen);
ps.print(" -n ");
ps.print(nrtest);
ps.print(" -f ");
ps.print(logfile + "\n");
ps.flush();
ps.close();
r = new Random(seed);
et = new ExecTest(maxlen, r);
try {
et.testValid();
} catch(Exception e) {
throw new RuntimeException("testValid: unexpected exception", e);
}
for (int i = 0; i < nrtest; i++) {
Exception e = null;
et.clearException();
try {
et.test();
e = new RuntimeException("Expected exception - got none");
} catch (IOException o) {
if (et.pendingException())
e = new RuntimeException("Expected exception - got IO", o);
} catch (JTaintException s) {
if (!et.pendingException())
e = new RuntimeException("Unexpected JTaintException", s);
} catch (Throwable x) {
e = new RuntimeException("Unexpected other exception", x);
}
if (e != null) {
System.out.println("FAILURE -- Op " + currentOp);
if (currentCommand != null) {
System.out.println("Current command: " + currentCommand);
System.out.println("Taint: " +
currentCommand.@internal@taint());
}
if (currentCommandArray != null) {
for (int j = 0; j < currentCommandArray.length; j++) {
System.out.println("CommandArray[" + j + "]: " +
currentCommandArray[j]);
System.out.println("Taint: " +
currentCommandArray[j].@internal@taint());
}
}
if (currentCommandList != null) {
for (int j = 0; j < currentCommandList.size(); j++) {
String s = (String) currentCommandList.get(j);
System.out.println("CommandList[" + j + "]: " + s);
System.out.println("Taint: " + s.@internal@taint());
}
}
e.printStackTrace();
System.exit(-1);
}
}
if (Log.hasError() || Log.hasWarning() || Log.hasVuln()) {
System.out.println("Error or Warning encountered -- check logs");
System.exit(-1);
}
}
}