Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*************************************************************************
* *
* CESeCore: CE Security Core *
* *
* This software is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or any later version. *
* *
* See terms of license at gnu.org. *
* *
*************************************************************************/
package org.cesecore.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateParsingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.log4j.Logger;
import org.cesecore.internal.InternalResources;
/**
* Tools to handle calls with Java Process API ({@link https://docs.oracle.com/javase/8/docs/api/java/lang/Process.html}.
*
* @version $Id: ExternalProcessTools.java 27126 2017-12-16 09:28:54Z anjakobs $
*/
public final class ExternalProcessTools {
/** Class logger. */
private static final Logger log = Logger.getLogger(ExternalProcessTools.class);
/** Internal localization of logs and errors. */
private static final InternalResources intres = InternalResources.getInstance();
/** Literal for the (platform dependent) line separator. */
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
/** Literal for place holder for the certificate issued */
public static final String PLACE_HOLDER_CERTIFICATE = "%cert%";
/** Literal for default MS Windows shell. */
public static final String WINDOWS_SHELL = "cmd.exe";
/** Literal for default MS Windows shell options. */
public static final String WINDOWS_SHELL_OPTIONS = "/c";
/** Literal for default Unix shell. */
public static final String UNIX_SHELL = "/bin/sh";
/** Literal for default Unix shell options. */
public static final String UNIX_SHELL_OPTIONS = "-c";
/** Literal for exit code label / prefix. */
public static final String EXIT_CODE_PREFIX = "Exit code: ";
/** Literal for STDOUT label to log the external out streams . */
public static final String STDOUT_PREFIX = "STDOUT: ";
/** Literal for ERROUT label to log the external out streams . */
public static final String ERROUT_PREFIX = "ERROUT: ";
/**
* Builds the platform dependent external command array:
* - field at index 0 is the interpreter,
* - field at index 1 is the one and only parameter of the interpreter,
* - field at index 2 must contain the complete external command, including pipes, chains, sub-shells, etc. and is appended later.
*
* @param the external command or script
* @return the command array as list.
*/
protected static final List buildShellCommand(final String cmd) {
final List result = new ArrayList();
if (SystemUtils.IS_OS_WINDOWS) {
result.add(WINDOWS_SHELL);
result.add(WINDOWS_SHELL_OPTIONS);
} else {
result.add(UNIX_SHELL);
result.add(UNIX_SHELL_OPTIONS);
}
if (log.isDebugEnabled()) {
log.debug("Use platform shell command for " + SystemUtils.OS_NAME + " : " + result);
}
if (result.size() == 2) {
result.add(cmd);
}
return result;
}
/**
* Writes the byte-array to a temporary file or pipes the PEM certificate into the command, and launches the given external command,
* see {@link ExternalProcessTools#launchExternalCommand(String, byte[], boolean, boolean, boolean, boolean, List, String)}.
*
* @param cmd The command to run.
* @param bytes The buffer with content to write to the file.
* @param failOnCode Determines if the method should fail on a non-zero exit code.
* @param failOnOutput Determines if the method should fail on output to standard error.
* @param args Added to the command after the temporary files name
* @throws ExternalProcessException if the temporary file could not be written or the external process fails.
*/
public static final List launchExternalCommand(final String cmd, final byte[] bytes, final boolean failOnCode, final boolean failOnOutput,
final List args, final String filePrefix) throws ExternalProcessException {
return launchExternalCommand(cmd, bytes, failOnCode, failOnOutput, false, false, args, filePrefix);
}
/**
* Writes the byte-array to a temporary file and launches the given external command with the file as argument at
* index positional parameter index 1 or the pipes the PEM certificate into the command. The function will, depending on
* its parameters, fail if output to standard error from the command was detected or the command returns with an non-zero exit code.
*
* @param cmd The command to run. If the parameter place holder {@link #PLACE_HOLDER_CERTIFICATE} is used, the PEM certificate is piped into the STDIN of the command (i.e. 'openssl x509 -text -noout %cert%').
* @param bytes The buffer with content to write to the file.
* @param failOnCode Determines if the method should fail on a non-zero exit code.
* @param failOnOutput Determines if the method should fail on output to standard error.
* @param logStdOut if the scripts STDOUT should be logged as info.
* @param logErrOut if the scripts ERROUT should be logged as info.
* @param arguments Added to the command after the temporary files name
* @throws ExternalProcessException if the temporary file could not be written or the external process fails.
*/
public static final List launchExternalCommand(final String cmd, final byte[] bytes, final boolean failOnCode, final boolean failOnOutput,
final boolean logStdOut, final boolean logErrOut, final List arguments, final String filePrefix) throws ExternalProcessException {
final long startTime = System.currentTimeMillis();
int exitStatus = -1;
final List result = new ArrayList();
final boolean writeFileToDisk = !arguments.contains(PLACE_HOLDER_CERTIFICATE);
File file = null;
if (writeFileToDisk) {
file = writeTemporaryFileToDisk(bytes, filePrefix, ".tmp");
}
// Execute external script or command with PEM in STDIN or full path of temporary file as first argument.
String filename = null;
try {
final List cmdTokens = Arrays.asList(cmd.split("\\s"));
// Write file to disk or process place holder with PEM certificates and build shell command.
if (writeFileToDisk) {
filename = file.getCanonicalPath();
arguments.add(0, filename);
} else {
// Only works with PEM X.509 certificates at the time as used in ExternalCommandCertificateValidator (not by CRL publishers).
final List certificates = new ArrayList();
certificates.add(CertTools.getCertfromByteArray(bytes));
final byte[] testPemBytes = CertTools.getPemFromCertificateChain(certificates);
String pemString = new String(testPemBytes);
pemString = pemString.substring(pemString.indexOf(LINE_SEPARATOR) + 1, pemString.length());
pemString = pemString.substring(pemString.indexOf(LINE_SEPARATOR) + 1, pemString.length());
if (log.isDebugEnabled()) {
log.debug("Using certificates:\n" + pemString);
}
arguments.remove(arguments.indexOf(PLACE_HOLDER_CERTIFICATE));
if (SystemUtils.IS_OS_WINDOWS) {
// Broken. Command cannot be executed.
cmdTokens.set(0, "echo \"" + pemString + "\" | " + cmdTokens.get(0));
/*
* Hack needed for Windows, where Runtime.exec won't consistently encapsulate arguments, leading to arguments
* containing spaces (such as Subject DNs) sometimes being parsed as multiple arguments. Bash, on the other hand,
* won't parse quote surrounded arguments.
*/
qouteArguments(arguments);
} else {
cmdTokens.set(0, "echo -n \"" + pemString + "\" | " + cmdTokens.get(0));
}
}
List cmdArray = new ArrayList();
cmdArray.addAll(cmdTokens);
cmdArray.addAll(arguments);
if (!writeFileToDisk) {
cmdArray = buildShellCommand(StringUtils.join(cmdArray, " "));
}
if (log.isDebugEnabled()) {
log.debug("Process external command for " + getPlatformString() + ": " + cmdArray);
}
// Launch external process.
final Process externalProcess = Runtime.getRuntime().exec(cmdArray.toArray(new String[] {}), null, null);
externalProcess.getOutputStream().close(); // prevent process from trying to wait for user input (e.g. prompt for overwrite, or similar)
final BufferedReader stdError = new BufferedReader(new InputStreamReader(externalProcess.getErrorStream()));
final BufferedReader stdOut = new BufferedReader(new InputStreamReader(externalProcess.getInputStream()));
String line = null;
while ((line = stdOut.readLine()) != null) { // NOPMD: Required under win32 to avoid lock
if (logStdOut) {
result.add(STDOUT_PREFIX + line);
}
}
String stdErrorOutput = null;
// Check error code and the external applications output to STDERR.
exitStatus = externalProcess.waitFor();
result.add(0, EXIT_CODE_PREFIX + exitStatus);
if (((exitStatus != 0) && failOnCode) || (stdError.ready() && failOnOutput)) {
if (writeFileToDisk && file.exists()) {
file.delete();
}
String errTemp = null;
while (stdError.ready() && (errTemp = stdError.readLine()) != null) {
if (logErrOut) {
result.add(ERROUT_PREFIX + errTemp);
}
if (stdErrorOutput == null) {
stdErrorOutput = errTemp;
} else {
stdErrorOutput += "\n" + errTemp;
}
}
String msg = intres.getLocalizedMessage("process.errorexternalapp", cmd);
if (stdErrorOutput != null) {
msg += " - " + stdErrorOutput + " - " + filename;
}
throw new ExternalProcessException(msg, result);
}
} catch (CertificateParsingException | CertificateEncodingException e) { // Should never happen (is only used for certificates not for CRL.)
throw new ExternalProcessException("Certificate could not parsed or encoded." + cmd, e, result);
} catch (IOException e) { // if the command could not be found
result.add(0, EXIT_CODE_PREFIX + exitStatus);
if (logErrOut) {
result.add(ERROUT_PREFIX + e.getMessage());
}
throw new ExternalProcessException(intres.getLocalizedMessage("process.errorexternalapp", cmd), e, result);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new ExternalProcessException(intres.getLocalizedMessage("process.errorexternalapp", cmd), e, result);
} finally {
if (writeFileToDisk && file != null && file.exists() && !file.delete()) {
// Remove temporary file or schedule for delete if delete fails.
file.deleteOnExit();
log.info(intres.getLocalizedMessage("process.errordeletetempfile", filename));
}
}
if (log.isTraceEnabled()) {
log.trace("Time spent to execute external command (writeFileToDisk=" + writeFileToDisk + "): " + (System.currentTimeMillis() - startTime)
+ "ms.");
}
return result;
}
public static final String getPlatformString() {
return SystemUtils.OS_NAME + " / " + SystemUtils.OS_VERSION + " - " + SystemUtils.OS_ARCH;
}
/**
* Writes the byte array into a temporary file with the prefix + "-" + System.currentTimeMillies + suffix into the user directory and returns it, or null.
*
* @param bytes the bytes to write.
* @param filePrefix the file prefix.
* @param fileSuffix the file suffix.
* @return the file or null.
* @throws ExternalProcessException any exception.
*/
public static final File writeTemporaryFileToDisk(final byte[] bytes, final String filePrefix, final String fileSuffix)
throws ExternalProcessException {
File file = null;
try {
file = File.createTempFile(filePrefix + "-" + System.currentTimeMillis(), fileSuffix);
} catch (IOException e) {
final String msg = intres.getLocalizedMessage("process.errortempfile");
log.error(msg, e);
}
if (file != null) {
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(bytes);
} catch (FileNotFoundException e) {
final String msg = intres.getLocalizedMessage("process.errortempfile");
log.error(msg, e);
throw new ExternalProcessException(msg);
} catch (IOException e) {
try {
file.delete();
} catch (Exception e1) {
// NOOP
}
final String msg = intres.getLocalizedMessage("process.errortempfile");
log.error(msg, e);
throw new ExternalProcessException(msg);
}
}
return file;
}
/**
* Extracts the exit code in the list (at index 0 prefixed with #EXIT_CODE_PREFIX).
* @param out the output of the external process.
*
* @return the exit code.
*/
public static final Integer extractExitCode(final List out) {
Integer result = null;
if (CollectionUtils.isNotEmpty(out)) {
result = Integer.parseInt(out.get(0).replaceFirst(ExternalProcessTools.EXIT_CODE_PREFIX, StringUtils.EMPTY));
}
return result;
}
/**
* Checks if the list contains logging to ERROUT.
* @param out the output of the external process.
* @return true if the list contains logging to ERROUT.
*/
public static final boolean containsErrout(final List out) {
if (CollectionUtils.isNotEmpty(out) && out.size() > 1) {
for (int i= 1,j=out.size();i arguments) {
for (int i = 0; i < arguments.size(); i++) {
String argument = arguments.get(i);
//Add quotes to encapsulate argument.
if (!argument.startsWith("\"") && !argument.endsWith("\"")) {
arguments.set(i, "\"" + argument + "\"");
}
}
}
/**
* Avoid instantiation.
*/
private ExternalProcessTools() {
}
}