HIRS/HIRS_ProvisionerTPM2/src/Utils.cpp

431 lines
13 KiB
C++

/**
* Copyright (C) 2017-2018, U.S. Government
*/
#include <Utils.h>
#include <HirsRuntimeException.h>
#include <Process.h>
#include <sys/stat.h>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cctype>
#include <string>
#include <stdexcept>
#include <unordered_map>
#include <vector>
using std::hex;
using std::ifstream;
using std::ios;
using std::ofstream;
using std::out_of_range;
using std::remove;
using std::setfill;
using std::setw;
using std::string;
using std::stringstream;
using std::unordered_map;
using std::vector;
using hirs::exception::HirsRuntimeException;
namespace hirs {
namespace json_utils {
string JSONFieldParser::parseJsonStringField(const std::string &jsonObject,
const std::string &jsonFieldName) {
stringstream regexPatternStream;
regexPatternStream << "(?i)\\\""
<< jsonFieldName
<< "\\\"\\s*:\\s*\\\"(.*)\\\"";
string value;
if (RE2::PartialMatch(jsonObject, regexPatternStream.str(), &value)) {
return value;
} else {
return "";
}
}
} // namespace json_utils
namespace file_utils {
/**
* Returns whether or not the argument directory exists.
* @return True if the directory exists. False, otherwise.
*/
bool dirExists(const string& path) {
struct stat pathinfo;
if (stat(path.c_str(), &pathinfo) != 0) {
return false;
} else {
return (pathinfo.st_mode & S_IFDIR) != 0;
}
}
/**
* Returns whether or not the argument file name exists and is a
* regular file.
* @param filename file to check for existence
* @return True if the file exists. False, otherwise.
*/
bool fileExists(const string& filename) {
struct stat buffer;
if (stat(filename.c_str(), &buffer) != 0) {
return false;
} else {
return S_ISREG(buffer.st_mode);
}
}
string fileToString(const string& filename) {
stringstream ss;
ifstream t(filename);
if (!t.good()) {
stringstream ss;
ss << "Unable to open file: " << filename;
throw HirsRuntimeException(ss.str(),
"Utils.cpp::file_utils::fileToString");
}
ss << t.rdbuf();
t.close();
return ss.str();
}
/**
* Reads the contents of an entire file into a string. If
* the file can't be opened, the default value is returned.
* @param filename the name and path of the file to read.
* @param defaultVal the value to return if the file can't be
* opened.
* @return the contents of the specified file as a string.
*/
string fileToString(const string& filename, const string& defaultVal) {
stringstream ss;
ifstream t(filename);
if (!t.good()) {
return defaultVal;
}
ss << t.rdbuf();
t.close();
return ss.str();
}
string getFileAsOneLineOrEmptyString(const string& filename) {
return string_utils::trimNewLines(fileToString(filename, ""));
}
/**
* Takes a byte string and writes the contents to a file of the given name.
* @param bytes string bytes to write
* @param filename file name to be written to
*/
void writeBinaryFile(const string& bytes, const string& filename) {
ofstream file(filename, ios::out | ios::binary);
if (file.is_open()) {
file.write(bytes.c_str(), bytes.size());
file.close();
} else {
throw std::invalid_argument("Cannot write to specified file");
}
}
int getFileSize(const string& filename) {
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
return in.tellg();
}
// copy portion of file to temp file
void splitFile(const string& inFilename, const string& outFilename,
int startPos, int readSize) {
ifstream inStr(inFilename, ifstream::binary);
ofstream outStr(outFilename, ofstream::binary);
inStr.seekg(startPos);
char* fileBlock = new char[readSize];
inStr.read(fileBlock, readSize);
outStr.write(fileBlock, readSize);
inStr.close();
outStr.close();
delete[] fileBlock;
}
} // namespace file_utils
namespace string_utils {
string binaryToHex(const string& bin) {
stringstream output;
for (int i = 0; i < 20; i++) {
stringstream str;
str << hex << setfill('0') << setw(2) << static_cast<int>(bin[i]);
string dig = str.str();
if (dig.length() > 2) {
dig = dig.substr(dig.length() - 2, 2);
}
output << dig;
}
return output.str();
}
bool contains(const string& str, const string& substring) {
return str.find(substring) != string::npos;
}
string longToHex(const uint32_t& value) {
stringstream output;
output << "0x" << hex << value;
return output.str();
}
bool isHexString(const string& str) {
bool isHexStringFlag = !str.empty();
if (isHexStringFlag) {
auto startIndex = str.begin();
if (str.substr(0, 2) == "0x") {
startIndex += 2;
}
isHexStringFlag = std::all_of(startIndex, str.end(), isxdigit);
}
return isHexStringFlag;
}
string hexToBytes(const string& hexString) {
// if the string has an odd number of chars, return an empty string
if (!isHexString(hexString) || hexString.size() % 2 != 0) {
return {};
}
vector<uint8_t> bytes;
for (uint32_t i = 0; i < hexString.length(); i += 2) {
string byteString = hexString.substr(i, 2);
uint8_t byte = static_cast<uint8_t>(strtol(byteString.c_str(),
nullptr, 16));
bytes.push_back(byte);
}
return string(bytes.begin(), bytes.end());
}
uint32_t hexToLong(const string& hexString) {
uint32_t value;
stringstream conversionStream;
conversionStream << hexString;
conversionStream >> hex >> value;
return value;
}
string trimNewLines(string str) {
return trimChar(str, '\n');
}
string trimQuotes(string str) {
return trimChar(str, '\"');
}
string trimChar(string str, char targetChar) {
str.erase(remove(str.begin(), str.end(), targetChar), str.end());
return str;
}
string trimWhitespaceFromLeft(string str) {
RE2 pattern("^\\s+");
while (RE2::PartialMatch(str, pattern)) {
str = str.erase(0, 1);
}
return str;
}
string trimWhitespaceFromRight(string str) {
RE2 pattern("\\s+$");
while (RE2::PartialMatch(str, pattern)) {
str = str.erase(str.length()-1, 1);
}
return str;
}
string trimWhitespaceFromBothEnds(string str) {
return trimWhitespaceFromRight(trimWhitespaceFromLeft(str));
}
} // namespace string_utils
namespace tpm2_tools_utils {
const unordered_map<string, Tpm2ToolsVersion>
Tpm2ToolsVersionChecker::kVersionMap = {
{"1.1.0", Tpm2ToolsVersion::VERSION_1_1_0 },
{"2.1.0", Tpm2ToolsVersion::VERSION_2_1_0 },
{"3", Tpm2ToolsVersion::VERSION_3 }
};
const unordered_map<string, Tpm2ToolsVersion>
Tpm2ToolsVersionChecker::kMaxSupportedVersionMap = {
{"Ubuntu 17.10", Tpm2ToolsVersion::VERSION_1_1_0 },
{"Ubuntu 18.04", Tpm2ToolsVersion::VERSION_2_1_0 },
{"Ubuntu 18.10", Tpm2ToolsVersion::VERSION_2_1_0 },
{"CentOS Linux 7", Tpm2ToolsVersion::VERSION_3 }
};
Tpm2ToolsVersion Tpm2ToolsVersionChecker::findTpm2ToolsVersion() {
string versionOutput = RUN_PROCESS_OR_THROW("tpm2_nvlist", "-v");
string version = Tpm2ToolsOutputParser::parseTpm2ToolsVersion(
versionOutput);
string majorVersion = Tpm2ToolsOutputParser::parseTpm2ToolsMajorVersion(
version);
if (!version.empty()) {
try {
// Look to see if tpm2-tools major version is supported
return kVersionMap.at(majorVersion);
}
catch (const out_of_range& oor) {
// If major version not supported, then look for specific version
try {
return kVersionMap.at(version);
}
catch (const out_of_range& oor) {
// If no version found, version is unsupported, throw exception
stringstream ss;
ss << "Unsupported Tpm2 Tools Version Detected: " << version;
throw HirsRuntimeException(ss.str(),
"Tpm2ToolsVersionChecker::findTpm2ToolsVersion");
}
}
} else {
// If version check returns empty, instead of throwing exception,
// then tpm2-tools is installed but version lookup is faulty.
// Get current runtime environment distribution.
string currentDistribution = getDistribution();
try {
// Look to see if current distribution has a supported version
// and use that as best guess at version number
return kMaxSupportedVersionMap.at(currentDistribution);
} catch (const out_of_range& oor) {
stringstream ss;
ss << "Unsupported Distribution Detected: " << currentDistribution;
throw HirsRuntimeException(ss.str(),
"Tpm2ToolsVersionChecker::findTpm2ToolsVersion");
}
}
}
string Tpm2ToolsVersionChecker::getDistribution() {
stringstream completeDistro;
string distribution;
string distributionRelease;
ifstream releaseFile;
string line;
releaseFile.open("/etc/os-release");
if (releaseFile.is_open()) {
while (getline(releaseFile, line)) {
stringstream ss(line);
string item;
vector<string> tokens;
while (getline(ss, item, '=')) {
tokens.push_back(item);
}
if (!tokens.empty() && tokens.at(0) == "NAME") {
distribution = string_utils::trimQuotes(tokens.at(1));
} else if (!tokens.empty() && tokens.at(0) == "VERSION_ID") {
distributionRelease = string_utils::trimQuotes(tokens.at(1));
}
}
completeDistro << distribution << " " << distributionRelease;
releaseFile.close();
}
return completeDistro.str();
}
uint16_t Tpm2ToolsOutputParser::parseNvDataSize(const string &nvHandle,
const string &nvListOutput) {
stringstream regexPatternStream;
regexPatternStream << nvHandle
<< "(?:.*\\n)+?"
<< "(?i).*size\\S*:\\s*([0-9]+)";
uint16_t dataSize;
if (RE2::PartialMatch(nvListOutput, regexPatternStream.str(), &dataSize)) {
return dataSize;
} else {
return 0;
}
}
string Tpm2ToolsOutputParser::parseNvReadOutput(const string &nvReadOutput) {
stringstream regexPatternStream;
regexPatternStream << ".*\\n*The size of data:[0-9]+";
string byteString = nvReadOutput;
// Remove tpm2_nvlist header
int numReplacements = RE2::GlobalReplace(&byteString,
regexPatternStream.str(), "");
if (numReplacements != 0) {
// Remove any non-hexadecimal characters
RE2::GlobalReplace(&byteString, "[^0-9A-Fa-f]+", "");
return string_utils::hexToBytes(byteString);
} else {
return "";
}
}
bool Tpm2ToolsOutputParser::parsePersistentObjectExists(
const std::string &handle,
const std::string &listPersistentOutput) {
stringstream regexPatternStream;
regexPatternStream << "(?i)Persistent.*handle.*:\\s*"
<< handle;
return RE2::PartialMatch(listPersistentOutput, regexPatternStream.str());
}
string Tpm2ToolsOutputParser::parseTpmErrorCode(const string& toolOutput) {
stringstream regexPatternStream;
regexPatternStream << "(?i)Error.*:\\s*(0x[0-9a-fA-F]{3})";
string errorCode;
if (RE2::PartialMatch(toolOutput, regexPatternStream.str(), &errorCode)) {
return errorCode;
} else {
return "";
}
}
string Tpm2ToolsOutputParser::parseTpm2ToolsVersion(const string& toolOutput) {
stringstream regexPatternStream;
regexPatternStream << "(?i)version[^0-9]*([0-9]+\\.[0-9]+\\.[0-9]+).*";
string version;
if (RE2::PartialMatch(toolOutput, regexPatternStream.str(), &version)) {
return version;
} else {
return "";
}
}
string Tpm2ToolsOutputParser::parseTpm2ToolsMajorVersion(
const string& toolVersion) {
stringstream regexPatternStream;
regexPatternStream << "^([0-9]+)\\.[0-9]+\\.[0-9]+$";
string majorVersion;
if (RE2::PartialMatch(toolVersion, regexPatternStream.str(),
&majorVersion)) {
return majorVersion;
} else {
return "";
}
}
} // namespace tpm2_tools_utils
} // namespace hirs