1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #include "cipher_mgmt.hpp" 17 18 #include <fcntl.h> 19 #include <sys/stat.h> 20 #include <sys/types.h> 21 #include <unistd.h> 22 23 #include <filesystem> 24 #include <fstream> 25 #include <phosphor-logging/log.hpp> 26 27 namespace ipmi 28 { 29 30 using namespace phosphor::logging; 31 using Json = nlohmann::json; 32 namespace fs = std::filesystem; 33 34 CipherConfig& getCipherConfigObject(const std::string& csFileName, 35 const std::string& csDefaultFileName) 36 { 37 static CipherConfig cipherConfig(csFileName, csDefaultFileName); 38 return cipherConfig; 39 } 40 41 CipherConfig::CipherConfig(const std::string& csFileName, 42 const std::string& csDefaultFileName) : 43 cipherSuitePrivFileName(csFileName), 44 cipherSuiteDefaultPrivFileName(csDefaultFileName) 45 { 46 loadCSPrivilegesToMap(); 47 } 48 49 void CipherConfig::loadCSPrivilegesToMap() 50 { 51 if (!fs::exists(cipherSuiteDefaultPrivFileName)) 52 { 53 log<level::ERR>("CS privilege levels default file does not exist..."); 54 } 55 else 56 { 57 // read default privileges 58 Json data = readCSPrivilegeLevels(cipherSuiteDefaultPrivFileName); 59 60 // load default privileges 61 updateCSPrivilegesMap(data); 62 63 // check for user-saved privileges 64 if (fs::exists(cipherSuitePrivFileName)) 65 { 66 data = readCSPrivilegeLevels(cipherSuitePrivFileName); 67 if (data != nullptr) 68 { 69 // update map with user-saved privileges by merging (overriding) 70 // values from the defaults 71 updateCSPrivilegesMap(data); 72 } 73 } 74 } 75 } 76 77 void CipherConfig::updateCSPrivilegesMap(const Json& jsonData) 78 { 79 for (uint8_t chNum = 0; chNum < ipmi::maxIpmiChannels; chNum++) 80 { 81 std::string chKey = "Channel" + std::to_string(chNum); 82 for (uint8_t csNum = 0; csNum < maxCSRecords; csNum++) 83 { 84 auto csKey = "CipherID" + std::to_string(csNum); 85 86 if (jsonData.find(chKey) != jsonData.end()) 87 { 88 csPrivilegeMap[{chNum, csNum}] = convertToPrivLimitIndex( 89 static_cast<std::string>(jsonData[chKey][csKey])); 90 } 91 } 92 } 93 } 94 95 Json CipherConfig::readCSPrivilegeLevels(const std::string& csFileName) 96 { 97 std::ifstream jsonFile(csFileName); 98 if (!jsonFile.good()) 99 { 100 log<level::ERR>("JSON file not found"); 101 return nullptr; 102 } 103 104 Json data = nullptr; 105 try 106 { 107 data = Json::parse(jsonFile, nullptr, false); 108 } 109 catch (const Json::parse_error& e) 110 { 111 log<level::ERR>("Corrupted cipher suite privilege levels config file.", 112 entry("MSG: %s", e.what())); 113 } 114 115 return data; 116 } 117 118 int CipherConfig::writeCSPrivilegeLevels(const Json& jsonData) 119 { 120 std::string tmpFile = 121 static_cast<std::string>(cipherSuitePrivFileName) + "_tmpXXXXXX"; 122 123 char tmpRandomFile[tmpFile.length() + 1]; 124 strncpy(tmpRandomFile, tmpFile.c_str(), tmpFile.length() + 1); 125 126 int fd = mkstemp(tmpRandomFile); 127 fchmod(fd, 0644); 128 129 if (fd < 0) 130 { 131 log<level::ERR>("Error opening CS privilege level config file", 132 entry("FILE_NAME=%s", tmpFile.c_str())); 133 return -EIO; 134 } 135 const auto& writeData = jsonData.dump(); 136 if (write(fd, writeData.c_str(), writeData.size()) != 137 static_cast<ssize_t>(writeData.size())) 138 { 139 close(fd); 140 log<level::ERR>("Error writing CS privilege level config file", 141 entry("FILE_NAME=%s", tmpFile.c_str())); 142 unlink(tmpRandomFile); 143 return -EIO; 144 } 145 close(fd); 146 147 if (std::rename(tmpRandomFile, cipherSuitePrivFileName.c_str())) 148 { 149 log<level::ERR>("Error renaming CS privilege level config file", 150 entry("FILE_NAME=%s", tmpFile.c_str())); 151 unlink(tmpRandomFile); 152 return -EIO; 153 } 154 155 return 0; 156 } 157 158 uint4_t CipherConfig::convertToPrivLimitIndex(const std::string& value) 159 { 160 auto iter = std::find(ipmi::privList.begin(), ipmi::privList.end(), value); 161 if (iter == privList.end()) 162 { 163 log<level::ERR>("Invalid privilege.", 164 entry("PRIV_STR=%s", value.c_str())); 165 return ccUnspecifiedError; 166 } 167 168 return static_cast<uint4_t>(std::distance(ipmi::privList.begin(), iter)); 169 } 170 171 std::string CipherConfig::convertToPrivLimitString(const uint4_t& value) 172 { 173 return ipmi::privList.at(static_cast<size_t>(value)); 174 } 175 176 ipmi::Cc CipherConfig::getCSPrivilegeLevels( 177 uint8_t chNum, std::array<uint4_t, maxCSRecords>& csPrivilegeLevels) 178 { 179 if (!isValidChannel(chNum)) 180 { 181 log<level::ERR>("Invalid channel number", entry("CHANNEL=%u", chNum)); 182 return ccInvalidFieldRequest; 183 } 184 185 for (size_t csNum = 0; csNum < maxCSRecords; ++csNum) 186 { 187 csPrivilegeLevels[csNum] = csPrivilegeMap[{chNum, csNum}]; 188 } 189 return ccSuccess; 190 } 191 192 ipmi::Cc CipherConfig::setCSPrivilegeLevels( 193 uint8_t chNum, const std::array<uint4_t, maxCSRecords>& requestData) 194 { 195 if (!isValidChannel(chNum)) 196 { 197 log<level::ERR>("Invalid channel number", entry("CHANNEL=%u", chNum)); 198 return ccInvalidFieldRequest; 199 } 200 201 Json jsonData; 202 if (!fs::exists(cipherSuitePrivFileName)) 203 { 204 log<level::INFO>("CS privilege levels user settings file does not " 205 "exist. Creating..."); 206 } 207 else 208 { 209 jsonData = readCSPrivilegeLevels(cipherSuitePrivFileName); 210 if (jsonData == nullptr) 211 { 212 return ccUnspecifiedError; 213 } 214 } 215 216 Json privData; 217 std::string csKey; 218 constexpr auto privMaxValue = static_cast<uint8_t>(ipmi::Privilege::Oem); 219 for (size_t csNum = 0; csNum < maxCSRecords; ++csNum) 220 { 221 csKey = "CipherID" + std::to_string(csNum); 222 auto priv = static_cast<uint8_t>(requestData[csNum]); 223 224 if (priv > privMaxValue) 225 { 226 return ccInvalidFieldRequest; 227 } 228 privData[csKey] = convertToPrivLimitString(priv); 229 } 230 231 std::string chKey = "Channel" + std::to_string(chNum); 232 jsonData[chKey] = privData; 233 234 if (writeCSPrivilegeLevels(jsonData)) 235 { 236 log<level::ERR>("Error in setting CS Privilege Levels."); 237 return ccUnspecifiedError; 238 } 239 240 updateCSPrivilegesMap(jsonData); 241 return ccSuccess; 242 } 243 244 } // namespace ipmi 245