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/lg2.hpp>
24
25 #include <filesystem>
26 #include <fstream>
27
28 namespace ipmi
29 {
30
31 using Json = nlohmann::json;
32 namespace fs = std::filesystem;
33
getCipherConfigObject(const std::string & csFileName,const std::string & csDefaultFileName)34 CipherConfig& getCipherConfigObject(const std::string& csFileName,
35 const std::string& csDefaultFileName)
36 {
37 static CipherConfig cipherConfig(csFileName, csDefaultFileName);
38 return cipherConfig;
39 }
40
CipherConfig(const std::string & csFileName,const std::string & csDefaultFileName)41 CipherConfig::CipherConfig(const std::string& csFileName,
42 const std::string& csDefaultFileName) :
43 cipherSuitePrivFileName(csFileName),
44 cipherSuiteDefaultPrivFileName(csDefaultFileName)
45 {
46 loadCSPrivilegesToMap();
47 }
48
loadCSPrivilegesToMap()49 void CipherConfig::loadCSPrivilegesToMap()
50 {
51 if (!fs::exists(cipherSuiteDefaultPrivFileName))
52 {
53 lg2::error("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
updateCSPrivilegesMap(const Json & jsonData)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
readCSPrivilegeLevels(const std::string & csFileName)95 Json CipherConfig::readCSPrivilegeLevels(const std::string& csFileName)
96 {
97 std::ifstream jsonFile(csFileName);
98 if (!jsonFile.good())
99 {
100 lg2::error("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 lg2::error(
112 "Corrupted cipher suite privilege levels config file: {ERROR}",
113 "ERROR", e);
114 }
115
116 return data;
117 }
118
writeCSPrivilegeLevels(const Json & jsonData)119 int CipherConfig::writeCSPrivilegeLevels(const Json& jsonData)
120 {
121 std::string tmpFile =
122 static_cast<std::string>(cipherSuitePrivFileName) + "_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 lg2::error("Error opening CS privilege level config file: {FILE_NAME}",
133 "FILE_NAME", tmpFile);
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 lg2::error("Error writing CS privilege level config file: {FILE_NAME}",
142 "FILE_NAME", tmpFile);
143 unlink(tmpRandomFile.data());
144 return -EIO;
145 }
146 close(fd);
147
148 if (std::rename(tmpRandomFile.data(), cipherSuitePrivFileName.c_str()))
149 {
150 lg2::error("Error renaming CS privilege level config file: {FILE_NAME}",
151 "FILE_NAME", tmpFile);
152 unlink(tmpRandomFile.data());
153 return -EIO;
154 }
155
156 return 0;
157 }
158
convertToPrivLimitIndex(const std::string & value)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 lg2::error("Invalid privilege: {PRIV_STR}", "PRIV_STR", value);
165 return ccUnspecifiedError;
166 }
167
168 return static_cast<uint4_t>(std::distance(ipmi::privList.begin(), iter));
169 }
170
convertToPrivLimitString(const uint4_t & value)171 std::string CipherConfig::convertToPrivLimitString(const uint4_t& value)
172 {
173 return ipmi::privList.at(static_cast<size_t>(value));
174 }
175
getCSPrivilegeLevels(uint8_t chNum,std::array<uint4_t,maxCSRecords> & csPrivilegeLevels)176 ipmi::Cc CipherConfig::getCSPrivilegeLevels(
177 uint8_t chNum, std::array<uint4_t, maxCSRecords>& csPrivilegeLevels)
178 {
179 if (!isValidChannel(chNum))
180 {
181 lg2::error(
182 "Get CS Privilege levels - Invalid channel number: {CHANNEL}",
183 "CHANNEL", chNum);
184 return ccInvalidFieldRequest;
185 }
186
187 for (size_t csNum = 0; csNum < maxCSRecords; ++csNum)
188 {
189 csPrivilegeLevels[csNum] = csPrivilegeMap[{chNum, csNum}];
190 }
191 return ccSuccess;
192 }
193
setCSPrivilegeLevels(uint8_t chNum,const std::array<uint4_t,maxCSRecords> & requestData)194 ipmi::Cc CipherConfig::setCSPrivilegeLevels(
195 uint8_t chNum, const std::array<uint4_t, maxCSRecords>& requestData)
196 {
197 if (!isValidChannel(chNum))
198 {
199 lg2::error(
200 "Set CS Privilege levels - Invalid channel number: {CHANNEL}",
201 "CHANNEL", chNum);
202 return ccInvalidFieldRequest;
203 }
204
205 Json jsonData;
206 if (!fs::exists(cipherSuitePrivFileName))
207 {
208 lg2::info("CS privilege levels user settings file does not "
209 "exist. Creating...");
210 }
211 else
212 {
213 jsonData = readCSPrivilegeLevels(cipherSuitePrivFileName);
214 if (jsonData == nullptr)
215 {
216 return ccUnspecifiedError;
217 }
218 }
219
220 Json privData;
221 std::string csKey;
222 constexpr auto privMaxValue = static_cast<uint8_t>(ipmi::Privilege::Oem);
223 for (size_t csNum = 0; csNum < maxCSRecords; ++csNum)
224 {
225 csKey = "CipherID" + std::to_string(csNum);
226 auto priv = static_cast<uint8_t>(requestData[csNum]);
227
228 if (priv > privMaxValue)
229 {
230 return ccInvalidFieldRequest;
231 }
232 privData[csKey] = convertToPrivLimitString(priv);
233 }
234
235 std::string chKey = "Channel" + std::to_string(chNum);
236 jsonData[chKey] = privData;
237
238 if (writeCSPrivilegeLevels(jsonData))
239 {
240 lg2::error("Error in setting CS Privilege Levels.");
241 return ccUnspecifiedError;
242 }
243
244 updateCSPrivilegesMap(jsonData);
245 return ccSuccess;
246 }
247
248 } // namespace ipmi
249