mirror of
https://github.com/nsacyber/HIRS.git
synced 2025-01-13 16:29:45 +00:00
6f99a10ad3
There were unnecessary references to file paths in the executable.
180 lines
5.9 KiB
C++
180 lines
5.9 KiB
C++
/**
|
|
* Copyright (C) 2017-2018, U.S. Government
|
|
*/
|
|
#include <Process.h>
|
|
#include <Logger.h>
|
|
#include <HirsRuntimeException.h>
|
|
#include <Utils.h>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <dirent.h>
|
|
|
|
#include <cstdio>
|
|
#include <cerrno>
|
|
#include <cstring>
|
|
|
|
#include <sstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
using hirs::exception::HirsRuntimeException;
|
|
using hirs::log::Logger;
|
|
using hirs::utils::Process;
|
|
using hirs::file_utils::dirExists;
|
|
using hirs::file_utils::fileExists;
|
|
using hirs::file_utils::fileToString;
|
|
using hirs::file_utils::trimFilenameFromPath;
|
|
using hirs::string_utils::trimChar;
|
|
using std::cerr;
|
|
using std::endl;
|
|
using std::ostream;
|
|
using std::stringstream;
|
|
using std::string;
|
|
using std::to_string;
|
|
|
|
const Logger Process::LOGGER = Logger::getDefaultLogger();
|
|
|
|
const char* const Process::kPgrepCommand = "pgrep";
|
|
const int Process::kMaxStatFileProcessNameLength = 15;
|
|
|
|
/**
|
|
* Constructor.
|
|
* @param executable the executable to be run
|
|
* @param arguments the arguments including options to be passed to the
|
|
* executable (defaults to empty string)
|
|
*/
|
|
Process::Process(const string& executable, const string& arguments)
|
|
: executable(executable), arguments(arguments) {
|
|
}
|
|
|
|
/**
|
|
* Run the command and return the return value of the process.
|
|
* @return the return value of the Linux process (between 0-255)
|
|
*/
|
|
int Process::run() {
|
|
fflush(nullptr); // Flush output streams of preexisting buffered output.
|
|
stringstream commandStringStream;
|
|
commandStringStream << executable;
|
|
if (!arguments.empty()) {
|
|
commandStringStream << " " << arguments;
|
|
}
|
|
|
|
string command = commandStringStream.str();
|
|
LOGGER.info("Executing command: " + command);
|
|
FILE* f = popen(command.c_str(), "re");
|
|
if (f == nullptr) {
|
|
stringstream errStream;
|
|
errStream << "Unable to open output stream from command \""
|
|
<< command << "\":" << strerror(errno) << endl;
|
|
LOGGER.error(errStream.str());
|
|
return -1;
|
|
}
|
|
int c;
|
|
stringstream ss;
|
|
while ((c = getc(f)) != EOF) {
|
|
ss << static_cast<char>(c);
|
|
}
|
|
|
|
output = ss.str();
|
|
|
|
// Linux return values are 0-255 even though pclose() returns a 32-bit int
|
|
uint16_t retValAsBigEndian = static_cast<uint16_t>(pclose(f));
|
|
return ntohs(retValAsBigEndian);
|
|
}
|
|
|
|
/**
|
|
* Run the command and return the return value of the process.
|
|
* @param osForErrorLogging ostream to collect error message on failure
|
|
* @return the return value of the Linux process (between 0-255)
|
|
*/
|
|
int Process::run(ostream& osForErrorLogging) {
|
|
int processReturnValue = run();
|
|
if (processReturnValue != 0) {
|
|
osForErrorLogging << "Call to " << executable
|
|
<< " returned " << processReturnValue << endl;
|
|
}
|
|
|
|
if (processReturnValue == 127) {
|
|
osForErrorLogging << "Is " << executable << " in your path?" << endl;
|
|
}
|
|
return processReturnValue;
|
|
}
|
|
|
|
/**
|
|
* Return a string containing the standard output stream of the Linux process.
|
|
* @return a string containing the standard output stream of the Linux process
|
|
*/
|
|
string Process::getOutputString() const {
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Static function for calling a process that must succeed or throw
|
|
* a HirsRuntimeException. This function is meant to be used with the
|
|
* RUN_PROCESS_OR_THROW macro in order to capture the source file name
|
|
* and source file line number for use in the exception message.
|
|
*
|
|
* @param executable the executable to be run
|
|
* @param arguments the arguments including options to be passed to the
|
|
* @param sourceFileName source file from which this method was called
|
|
* @param sourceLineNumber line number of source file from which this method
|
|
* was called
|
|
* executable (defaults to empty string)
|
|
*/
|
|
string Process::run(const string& executable,
|
|
const string& arguments,
|
|
const string& sourceFileName,
|
|
int sourceLineNumber) {
|
|
stringstream errorStream;
|
|
Process p(executable, arguments);
|
|
if (p.run(errorStream) != 0) {
|
|
errorStream << "\n\n"
|
|
<< "Process Output: "
|
|
<< p.getOutputString();
|
|
throw HirsRuntimeException(errorStream.str(),
|
|
sourceFileName + ": " + to_string(sourceLineNumber));
|
|
}
|
|
|
|
// Remove trailing newline if one exists
|
|
string str = p.getOutputString();
|
|
if (!str.empty() && str[str.length() - 1] == '\n') {
|
|
str.erase(str.length() - 1);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/**
|
|
* Static function to check if a specified process is currently running in the
|
|
* local environment.
|
|
*
|
|
* @param executable the executable to check is running
|
|
* @return true, if executable is found to be running / false, otherwise
|
|
*/
|
|
bool Process::isRunning(const string& executable) {
|
|
// Check if executable includes path and trim to just process name if so
|
|
string processName = trimFilenameFromPath(executable);
|
|
// Log warning about including path to executable
|
|
if (processName.length() < executable.length()) {
|
|
stringstream warnStream;
|
|
warnStream << "Including the path to an executable isn't recommended "
|
|
<< "as this has no bearing on whether it's determined to "
|
|
<< "be running in the local environment.";
|
|
LOGGER.warn(warnStream.str());
|
|
}
|
|
|
|
// Sanitize any attempts to hijack the process
|
|
processName = trimChar(processName, ';');
|
|
|
|
// If the process name is longer than 15 characters log a warning
|
|
if (processName.length() > kMaxStatFileProcessNameLength) {
|
|
stringstream warnStream;
|
|
warnStream << "A process name with length greater than "
|
|
<< to_string(kMaxStatFileProcessNameLength)
|
|
<< " may result in a false negative depending on the"
|
|
<< " current runtime environment.";
|
|
LOGGER.warn(warnStream.str());
|
|
}
|
|
return Process(kPgrepCommand, "-c " + processName).run() == 0;
|
|
}
|