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