blob: a2f024b2f9fd8021c457537910a9bf31a7365aa5 [file] [log] [blame]
/*
// Copyright (c) 2018 Intel Corporation
//
// 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.
*/
#include "cipher_mgmt.hpp"
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <phosphor-logging/log.hpp>
#include <filesystem>
#include <fstream>
namespace ipmi
{
using namespace phosphor::logging;
using Json = nlohmann::json;
namespace fs = std::filesystem;
CipherConfig& getCipherConfigObject(const std::string& csFileName,
const std::string& csDefaultFileName)
{
static CipherConfig cipherConfig(csFileName, csDefaultFileName);
return cipherConfig;
}
CipherConfig::CipherConfig(const std::string& csFileName,
const std::string& csDefaultFileName) :
cipherSuitePrivFileName(csFileName),
cipherSuiteDefaultPrivFileName(csDefaultFileName)
{
loadCSPrivilegesToMap();
}
void CipherConfig::loadCSPrivilegesToMap()
{
if (!fs::exists(cipherSuiteDefaultPrivFileName))
{
log<level::ERR>("CS privilege levels default file does not exist...");
}
else
{
// read default privileges
Json data = readCSPrivilegeLevels(cipherSuiteDefaultPrivFileName);
// load default privileges
updateCSPrivilegesMap(data);
// check for user-saved privileges
if (fs::exists(cipherSuitePrivFileName))
{
data = readCSPrivilegeLevels(cipherSuitePrivFileName);
if (data != nullptr)
{
// update map with user-saved privileges by merging (overriding)
// values from the defaults
updateCSPrivilegesMap(data);
}
}
}
}
void CipherConfig::updateCSPrivilegesMap(const Json& jsonData)
{
for (uint8_t chNum = 0; chNum < ipmi::maxIpmiChannels; chNum++)
{
std::string chKey = "Channel" + std::to_string(chNum);
for (uint8_t csNum = 0; csNum < maxCSRecords; csNum++)
{
auto csKey = "CipherID" + std::to_string(csNum);
if (jsonData.find(chKey) != jsonData.end())
{
csPrivilegeMap[{chNum, csNum}] = convertToPrivLimitIndex(
static_cast<std::string>(jsonData[chKey][csKey]));
}
}
}
}
Json CipherConfig::readCSPrivilegeLevels(const std::string& csFileName)
{
std::ifstream jsonFile(csFileName);
if (!jsonFile.good())
{
log<level::ERR>("JSON file not found");
return nullptr;
}
Json data = nullptr;
try
{
data = Json::parse(jsonFile, nullptr, false);
}
catch (const Json::parse_error& e)
{
log<level::ERR>("Corrupted cipher suite privilege levels config file.",
entry("MSG: %s", e.what()));
}
return data;
}
int CipherConfig::writeCSPrivilegeLevels(const Json& jsonData)
{
std::string tmpFile = static_cast<std::string>(cipherSuitePrivFileName) +
"_tmpXXXXXX";
std::vector<char> tmpRandomFile(tmpFile.length() + 1);
strncpy(tmpRandomFile.data(), tmpFile.c_str(), tmpFile.length() + 1);
int fd = mkstemp(tmpRandomFile.data());
fchmod(fd, 0644);
if (fd < 0)
{
log<level::ERR>("Error opening CS privilege level config file",
entry("FILE_NAME=%s", tmpFile.c_str()));
return -EIO;
}
const auto& writeData = jsonData.dump();
if (write(fd, writeData.c_str(), writeData.size()) !=
static_cast<ssize_t>(writeData.size()))
{
close(fd);
log<level::ERR>("Error writing CS privilege level config file",
entry("FILE_NAME=%s", tmpFile.c_str()));
unlink(tmpRandomFile.data());
return -EIO;
}
close(fd);
if (std::rename(tmpRandomFile.data(), cipherSuitePrivFileName.c_str()))
{
log<level::ERR>("Error renaming CS privilege level config file",
entry("FILE_NAME=%s", tmpFile.c_str()));
unlink(tmpRandomFile.data());
return -EIO;
}
return 0;
}
uint4_t CipherConfig::convertToPrivLimitIndex(const std::string& value)
{
auto iter = std::find(ipmi::privList.begin(), ipmi::privList.end(), value);
if (iter == privList.end())
{
log<level::ERR>("Invalid privilege.",
entry("PRIV_STR=%s", value.c_str()));
return ccUnspecifiedError;
}
return static_cast<uint4_t>(std::distance(ipmi::privList.begin(), iter));
}
std::string CipherConfig::convertToPrivLimitString(const uint4_t& value)
{
return ipmi::privList.at(static_cast<size_t>(value));
}
ipmi::Cc CipherConfig::getCSPrivilegeLevels(
uint8_t chNum, std::array<uint4_t, maxCSRecords>& csPrivilegeLevels)
{
if (!isValidChannel(chNum))
{
log<level::ERR>("Invalid channel number", entry("CHANNEL=%u", chNum));
return ccInvalidFieldRequest;
}
for (size_t csNum = 0; csNum < maxCSRecords; ++csNum)
{
csPrivilegeLevels[csNum] = csPrivilegeMap[{chNum, csNum}];
}
return ccSuccess;
}
ipmi::Cc CipherConfig::setCSPrivilegeLevels(
uint8_t chNum, const std::array<uint4_t, maxCSRecords>& requestData)
{
if (!isValidChannel(chNum))
{
log<level::ERR>("Invalid channel number", entry("CHANNEL=%u", chNum));
return ccInvalidFieldRequest;
}
Json jsonData;
if (!fs::exists(cipherSuitePrivFileName))
{
log<level::INFO>("CS privilege levels user settings file does not "
"exist. Creating...");
}
else
{
jsonData = readCSPrivilegeLevels(cipherSuitePrivFileName);
if (jsonData == nullptr)
{
return ccUnspecifiedError;
}
}
Json privData;
std::string csKey;
constexpr auto privMaxValue = static_cast<uint8_t>(ipmi::Privilege::Oem);
for (size_t csNum = 0; csNum < maxCSRecords; ++csNum)
{
csKey = "CipherID" + std::to_string(csNum);
auto priv = static_cast<uint8_t>(requestData[csNum]);
if (priv > privMaxValue)
{
return ccInvalidFieldRequest;
}
privData[csKey] = convertToPrivLimitString(priv);
}
std::string chKey = "Channel" + std::to_string(chNum);
jsonData[chKey] = privData;
if (writeCSPrivilegeLevels(jsonData))
{
log<level::ERR>("Error in setting CS Privilege Levels.");
return ccUnspecifiedError;
}
updateCSPrivilegesMap(jsonData);
return ccSuccess;
}
} // namespace ipmi