xref: /openbmc/phosphor-host-ipmid/user_channel/cipher_mgmt.cpp (revision a0e545d3be961c62975718121e48b8b07740e38a)
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