All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.owasp.esapi.reference.DefaultExecutor Maven / Gradle / Ivy

/**
 * OWASP Enterprise Security API (ESAPI)
 * 
 * This file is part of the Open Web Application Security Project (OWASP)
 * Enterprise Security API (ESAPI) project. For details, please see
 * http://www.owasp.org/index.php/ESAPI.
 *
 * Copyright (c) 2007 - The OWASP Foundation
 * 
 * The ESAPI is published by OWASP under the BSD license. You should read and accept the
 * LICENSE before you use, modify, and/or redistribute this software.
 * 
 * @author Jeff Williams Aspect Security
 * @created 2007
 */
package org.owasp.esapi.reference;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;

import org.owasp.esapi.ESAPI;
import org.owasp.esapi.ExecuteResult;
import org.owasp.esapi.Executor;
import org.owasp.esapi.Logger;
import org.owasp.esapi.codecs.Codec;
import org.owasp.esapi.codecs.UnixCodec;
import org.owasp.esapi.codecs.WindowsCodec;
import org.owasp.esapi.errors.ExecutorException;

/**
 * Reference implementation of the Executor interface. This implementation is very restrictive. Commands must exactly
 * equal the canonical path to an executable on the system. 
 * 
 * 

Valid characters for parameters are codec dependent, but will usually only include alphanumeric, forward-slash, and dash.

* * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 * @see org.owasp.esapi.Executor */ public class DefaultExecutor implements org.owasp.esapi.Executor { private static volatile Executor singletonInstance; public static Executor getInstance() { if ( singletonInstance == null ) { synchronized ( DefaultExecutor.class ) { if ( singletonInstance == null ) { singletonInstance = new DefaultExecutor(); } } } return singletonInstance; } /** The logger. */ private final Logger logger = ESAPI.getLogger("Executor"); private Codec codec = null; //private final int MAX_SYSTEM_COMMAND_LENGTH = 2500; /** * Instantiate a new Executor */ private DefaultExecutor() { if ( System.getProperty("os.name").indexOf("Windows") != -1 ) { logger.warning( Logger.SECURITY_SUCCESS, "Using WindowsCodec for Executor. If this is not running on Windows this could allow injection" ); codec = new WindowsCodec(); } else { logger.warning( Logger.SECURITY_SUCCESS, "Using UnixCodec for Executor. If this is not running on Unix this could allow injection" ); codec = new UnixCodec(); } } /** * {@inheritDoc} */ public ExecuteResult executeSystemCommand(File executable, List params) throws ExecutorException { File workdir = ESAPI.securityConfiguration().getWorkingDirectory(); boolean logParams = false; boolean redirectErrorStream = false; return executeSystemCommand( executable, params, workdir, codec, logParams, redirectErrorStream ); } /** * {@inheritDoc} * * The reference implementation sets the work directory, escapes the parameters as per the Codec in use, * and then executes the command without using concatenation. The exact, absolute, canonical path of each * executable must be listed as an approved executable in the ESAPI properties. The executable must also * exist on the disk. All failures will be logged, along with parameters if specified. Set the logParams to false if * you are going to invoke this interface with confidential information. */ public ExecuteResult executeSystemCommand(File executable, List params, File workdir, Codec codec, boolean logParams, boolean redirectErrorStream ) throws ExecutorException { try { // executable must exist if (!executable.exists()) { throw new ExecutorException("Execution failure", "No such executable: " + executable); } // executable must use canonical path if ( !executable.isAbsolute() ) { throw new ExecutorException("Execution failure", "Attempt to invoke an executable using a non-absolute path: " + executable); } // executable must use canonical path if ( !executable.getPath().equals( executable.getCanonicalPath() ) ) { throw new ExecutorException("Execution failure", "Attempt to invoke an executable using a non-canonical path: " + executable); } // exact, absolute, canonical path to executable must be listed in ESAPI configuration List approved = ESAPI.securityConfiguration().getAllowedExecutables(); if (!approved.contains(executable.getPath())) { throw new ExecutorException("Execution failure", "Attempt to invoke executable that is not listed as an approved executable in ESAPI configuration: " + executable.getPath() + " not listed in " + approved ); } // escape any special characters in the parameters for ( int i = 0; i < params.size(); i++ ) { String param = (String)params.get(i); params.set( i, ESAPI.encoder().encodeForOS(codec, param)); } // working directory must exist if (!workdir.exists()) { throw new ExecutorException("Execution failure", "No such working directory for running executable: " + workdir.getPath()); } // set the command into the list and create command array params.add(0, executable.getCanonicalPath()); // Legacy - this is how to implement in Java 1.4 // String[] command = (String[])params.toArray( new String[0] ); // Process process = Runtime.getRuntime().exec(command, new String[0], workdir); // The following is host to implement in Java 1.5+ ProcessBuilder pb = new ProcessBuilder(params); Map env = pb.environment(); env.clear(); // Security check - clear environment variables! pb.directory(workdir); pb.redirectErrorStream(redirectErrorStream); if ( logParams ) { logger.debug(Logger.SECURITY_SUCCESS, "Initiating executable: " + executable + " " + params + " in " + workdir); } else { logger.debug(Logger.SECURITY_SUCCESS, "Initiating executable: " + executable + " [sensitive parameters obscured] in " + workdir); } final StringBuilder outputBuffer = new StringBuilder(); final StringBuilder errorsBuffer = new StringBuilder(); final Process process = pb.start(); try { ReadThread errorReader; if (!redirectErrorStream) { errorReader = new ReadThread(process.getErrorStream(), errorsBuffer); errorReader.start(); } else { errorReader = null; } readStream( process.getInputStream(), outputBuffer ); if (errorReader != null) { errorReader.join(); if (errorReader.exception != null) { throw errorReader.exception; } } process.waitFor(); } catch (Throwable e) { process.destroy(); throw new ExecutorException("Execution failure", "Exception thrown during execution of system command: " + e.getMessage(), e); } String output = outputBuffer.toString(); String errors = errorsBuffer.toString(); int exitValue = process.exitValue(); if ( errors != null && errors.length() > 0 ) { String logErrors = errors; final int MAX_LEN = 256; if (logErrors.length() > MAX_LEN) { logErrors = logErrors.substring(0, MAX_LEN) + "(truncated at "+MAX_LEN+" characters)"; } logger.warning( Logger.SECURITY_SUCCESS, "Error during system command: " + logErrors ); } if ( exitValue != 0 ) { logger.warning( Logger.EVENT_FAILURE, "System command exited with non-zero status: " + exitValue ); } logger.debug(Logger.SECURITY_SUCCESS, "System command complete"); return new ExecuteResult(exitValue, output, errors); } catch (IOException e) { throw new ExecutorException("Execution failure", "Exception thrown during execution of system command: " + e.getMessage(), e); } } /** * readStream reads lines from an input stream and returns all of them in a single string * * @param is * input stream to read from * @throws IOException */ private static void readStream( InputStream is, StringBuilder sb ) throws IOException { InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { sb.append(line).append('\n'); } } private static class ReadThread extends Thread { volatile IOException exception; private final InputStream stream; private final StringBuilder buffer; ReadThread(InputStream stream, StringBuilder buffer) { this.stream = stream; this.buffer = buffer; } @Override public void run() { try { readStream(stream, buffer); } catch (IOException e) { exception = e; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy