| /* |
| // 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/lg2.hpp> |
| |
| #include <filesystem> |
| #include <fstream> |
| |
| namespace ipmi |
| { |
| |
| 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)) |
| { |
| lg2::error("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()) |
| { |
| lg2::error("JSON file not found"); |
| return nullptr; |
| } |
| |
| Json data = nullptr; |
| try |
| { |
| data = Json::parse(jsonFile, nullptr, false); |
| } |
| catch (const Json::parse_error& e) |
| { |
| lg2::error( |
| "Corrupted cipher suite privilege levels config file: {ERROR}", |
| "ERROR", e); |
| } |
| |
| 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) |
| { |
| lg2::error("Error opening CS privilege level config file: {FILE_NAME}", |
| "FILE_NAME", tmpFile); |
| return -EIO; |
| } |
| const auto& writeData = jsonData.dump(); |
| if (write(fd, writeData.c_str(), writeData.size()) != |
| static_cast<ssize_t>(writeData.size())) |
| { |
| close(fd); |
| lg2::error("Error writing CS privilege level config file: {FILE_NAME}", |
| "FILE_NAME", tmpFile); |
| unlink(tmpRandomFile.data()); |
| return -EIO; |
| } |
| close(fd); |
| |
| if (std::rename(tmpRandomFile.data(), cipherSuitePrivFileName.c_str())) |
| { |
| lg2::error("Error renaming CS privilege level config file: {FILE_NAME}", |
| "FILE_NAME", tmpFile); |
| 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()) |
| { |
| lg2::error("Invalid privilege: {PRIV_STR}", "PRIV_STR", value); |
| 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)) |
| { |
| lg2::error("Invalid channel number: {CHANNEL}", "CHANNEL", 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)) |
| { |
| lg2::error("Invalid channel number: {CHANNEL}", "CHANNEL", chNum); |
| return ccInvalidFieldRequest; |
| } |
| |
| Json jsonData; |
| if (!fs::exists(cipherSuitePrivFileName)) |
| { |
| lg2::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)) |
| { |
| lg2::error("Error in setting CS Privilege Levels."); |
| return ccUnspecifiedError; |
| } |
| |
| updateCSPrivilegesMap(jsonData); |
| return ccSuccess; |
| } |
| |
| } // namespace ipmi |