1 #include "file_io_type_cert.hpp"
2 
3 #include "common/utils.hpp"
4 
5 #include <libpldm/base.h>
6 #include <libpldm/oem/ibm/file_io.h>
7 #include <stdint.h>
8 
9 #include <phosphor-logging/lg2.hpp>
10 
11 PHOSPHOR_LOG2_USING;
12 
13 namespace pldm
14 {
15 using namespace utils;
16 
17 namespace responder
18 {
19 constexpr auto certObjPath = "/xyz/openbmc_project/certs/ca/entry/";
20 constexpr auto certEntryIntf = "xyz.openbmc_project.Certs.Entry";
21 static constexpr auto certFilePath = "/var/lib/ibm/bmcweb/";
22 
23 CertMap CertHandler::certMap;
24 
25 int CertHandler::writeFromMemory(uint32_t offset, uint32_t length,
26                                  uint64_t address,
27                                  oem_platform::Handler* /*oemPlatformHandler*/)
28 {
29     auto it = certMap.find(certType);
30     if (it == certMap.end())
31     {
32         error(
33             "Failed to find file type '{TYPE}' in certificate map. Write from memory during certificate exchange failed",
34             "TYPE", certType);
35         return PLDM_ERROR;
36     }
37 
38     auto fd = std::get<0>(it->second);
39     auto& remSize = std::get<1>(it->second);
40     auto rc = transferFileData(fd, false, offset, length, address);
41     if (rc == PLDM_SUCCESS)
42     {
43         remSize -= length;
44         if (!remSize)
45         {
46             close(fd);
47             certMap.erase(it);
48         }
49     }
50     return rc;
51 }
52 
53 int CertHandler::readIntoMemory(uint32_t offset, uint32_t length,
54                                 uint64_t address,
55                                 oem_platform::Handler* /*oemPlatformHandler*/)
56 {
57     std::string filePath = certFilePath;
58     filePath += "CSR_" + std::to_string(fileHandle);
59     if (certType != PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
60     {
61         return PLDM_ERROR_INVALID_DATA;
62     }
63     auto rc = transferFileData(filePath.c_str(), true, offset, length, address);
64     fs::remove(filePath);
65     if (rc)
66     {
67         return PLDM_ERROR;
68     }
69     return PLDM_SUCCESS;
70 }
71 
72 int CertHandler::read(uint32_t offset, uint32_t& length, Response& response,
73                       oem_platform::Handler* /*oemPlatformHandler*/)
74 {
75     info(
76         "Read file response for Sign CSR failed and file handle '{FILE_HANDLE}'",
77         "FILE_HANDLE", fileHandle);
78     std::string filePath = certFilePath;
79     filePath += "CSR_" + std::to_string(fileHandle);
80     if (certType != PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
81     {
82         return PLDM_ERROR_INVALID_DATA;
83     }
84     auto rc = readFile(filePath.c_str(), offset, length, response);
85     fs::remove(filePath);
86     if (rc)
87     {
88         return PLDM_ERROR;
89     }
90     return PLDM_SUCCESS;
91 }
92 
93 int CertHandler::write(const char* buffer, uint32_t offset, uint32_t& length,
94                        oem_platform::Handler* /*oemPlatformHandler*/)
95 {
96     auto it = certMap.find(certType);
97     if (it == certMap.end())
98     {
99         error(
100             "Failed to find file type '{TYPE}' in certificate map. Write during certificate exchange failed",
101             "TYPE", certType);
102         return PLDM_ERROR;
103     }
104 
105     auto fd = std::get<0>(it->second);
106     int rc = lseek(fd, offset, SEEK_SET);
107     if (rc == -1)
108     {
109         error(
110             "Failed to write certificate lseek at offset '{OFFSET}' of length '{LENGTH}', error number - {ERROR_NUM}",
111             "OFFSET", offset, "LENGTH", length, "ERROR_NUM", errno);
112         return PLDM_ERROR;
113     }
114     rc = ::write(fd, buffer, length);
115     if (rc == -1)
116     {
117         error(
118             "Failed to write certificate at offset '{OFFSET}' of length '{LENGTH}', error number - {ERROR_NUM}",
119             "LENGTH", length, "OFFSET", offset, "ERROR_NUM", errno);
120         return PLDM_ERROR;
121     }
122     length = rc;
123     auto& remSize = std::get<1>(it->second);
124     remSize -= length;
125     if (!remSize)
126     {
127         close(fd);
128         certMap.erase(it);
129     }
130 
131     if (certType == PLDM_FILE_TYPE_SIGNED_CERT)
132     {
133         constexpr auto certObjPath = "/xyz/openbmc_project/certs/ca/entry/";
134         constexpr auto certEntryIntf = "xyz.openbmc_project.Certs.Entry";
135 
136         std::string filePath = certFilePath;
137         filePath += "ClientCert_" + std::to_string(fileHandle);
138 
139         std::ifstream inFile;
140         inFile.open(filePath);
141         std::stringstream strStream;
142         strStream << inFile.rdbuf();
143         std::string str = strStream.str();
144         inFile.close();
145 
146         if (!str.empty())
147         {
148             PropertyValue value{str};
149 
150             DBusMapping dbusMapping{certObjPath + std::to_string(fileHandle),
151                                     certEntryIntf, "ClientCertificate",
152                                     "string"};
153             try
154             {
155                 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
156             }
157             catch (const std::exception& e)
158             {
159                 error(
160                     "Failed to write for set client certificate, error - {ERROR}",
161                     "ERROR", e);
162                 return PLDM_ERROR;
163             }
164             PropertyValue valueStatus{
165                 "xyz.openbmc_project.Certs.Entry.State.Complete"};
166             DBusMapping dbusMappingStatus{certObjPath +
167                                               std::to_string(fileHandle),
168                                           certEntryIntf, "Status", "string"};
169             try
170             {
171                 info(
172                     "Client certificate write status 'complete' for file handle '{FILE_HANDLE}'",
173                     "FILE_HANDLE", fileHandle);
174                 pldm::utils::DBusHandler().setDbusProperty(dbusMappingStatus,
175                                                            valueStatus);
176             }
177             catch (const std::exception& e)
178             {
179                 error(
180                     "Failed to write the set status property for certificate entry, error - {ERROR}",
181                     "ERROR", e);
182                 return PLDM_ERROR;
183             }
184             fs::remove(filePath);
185         }
186         else
187         {
188             PropertyValue value{"xyz.openbmc_project.Certs.Entry.State.BadCSR"};
189             DBusMapping dbusMapping{certObjPath + std::to_string(fileHandle),
190                                     certEntryIntf, "Status", "string"};
191             try
192             {
193                 info(
194                     "Client certificate write status 'Bad CSR' for file handle '{FILE_HANDLE}'",
195                     "FILE_HANDLE", fileHandle);
196                 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
197             }
198             catch (const std::exception& e)
199             {
200                 error(
201                     "Failed to write the set status property for certficate entry, error - {ERROR}",
202                     "ERROR", e);
203                 return PLDM_ERROR;
204             }
205         }
206     }
207     return PLDM_SUCCESS;
208 }
209 
210 int CertHandler::newFileAvailable(uint64_t length)
211 {
212     fs::create_directories(certFilePath);
213     fs::permissions(certFilePath,
214                     fs::perms::others_read | fs::perms::owner_write);
215     int fileFd = -1;
216     int flags = O_WRONLY | O_CREAT | O_TRUNC;
217     std::string filePath = certFilePath;
218 
219     if (certType == PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
220     {
221         return PLDM_ERROR_INVALID_DATA;
222     }
223     if (certType == PLDM_FILE_TYPE_SIGNED_CERT)
224     {
225         info(
226             "New file available for client certificate file with file handle {FILE_HANDLE}",
227             "FILE_HANDLE", fileHandle);
228         fileFd = open(
229             (filePath + "ClientCert_" + std::to_string(fileHandle)).c_str(),
230             flags, S_IRUSR | S_IWUSR);
231     }
232     else if (certType == PLDM_FILE_TYPE_ROOT_CERT)
233     {
234         fileFd = open((filePath + "RootCert").c_str(), flags,
235                       S_IRUSR | S_IWUSR);
236     }
237     if (fileFd == -1)
238     {
239         error(
240             "Failed to open new file available with file type '{TYPE}', error number - {ERROR_NUM}",
241             "TYPE", certType, "ERROR_NUM", errno);
242         return PLDM_ERROR;
243     }
244     certMap.emplace(certType, std::tuple(fileFd, length));
245     return PLDM_SUCCESS;
246 }
247 
248 int CertHandler::newFileAvailableWithMetaData(uint64_t length,
249                                               uint32_t metaDataValue1,
250                                               uint32_t /*metaDataValue2*/,
251                                               uint32_t /*metaDataValue3*/,
252                                               uint32_t /*metaDataValue4*/)
253 {
254     fs::create_directories(certFilePath);
255     fs::permissions(certFilePath,
256                     fs::perms::others_read | fs::perms::owner_write);
257     int fileFd = -1;
258     int flags = O_WRONLY | O_CREAT | O_TRUNC;
259     std::string filePath = certFilePath;
260 
261     if (certType == PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
262     {
263         return PLDM_ERROR_INVALID_DATA;
264     }
265     if (certType == PLDM_FILE_TYPE_SIGNED_CERT)
266     {
267         if (metaDataValue1 == PLDM_SUCCESS)
268         {
269             info(
270                 "Client certificate new file available with meta data for file handle '{FILE_HANDLE}'",
271                 "FILE_HANDLE", fileHandle);
272             fileFd = open(
273                 (filePath + "ClientCert_" + std::to_string(fileHandle)).c_str(),
274                 flags, S_IRUSR | S_IWUSR);
275         }
276         else if (metaDataValue1 == PLDM_INVALID_CERT_DATA)
277         {
278             error(
279                 "New file available with meta data for client certificate file has invalid data '{META_DATA}' with file handle '{FILE_HANDLE}'",
280                 "META_DATA", metaDataValue1, "FILE_HANDLE", fileHandle);
281             DBusMapping dbusMapping{certObjPath + std::to_string(fileHandle),
282                                     certEntryIntf, "Status", "string"};
283             std::string status = "xyz.openbmc_project.Certs.Entry.State.BadCSR";
284             PropertyValue value{status};
285             try
286             {
287                 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
288             }
289             catch (const std::exception& e)
290             {
291                 error(
292                     "Failed to set set status property of certificate entry in new file available with meta data, error - {ERROR}",
293                     "ERROR", e);
294                 return PLDM_ERROR;
295             }
296         }
297     }
298     else if (certType == PLDM_FILE_TYPE_ROOT_CERT)
299     {
300         fileFd = open((filePath + "RootCert").c_str(), flags,
301                       S_IRUSR | S_IWUSR);
302     }
303     if (fileFd == -1)
304     {
305         error(
306             "Failed to open file type '{TYPE}' but New file available with meta data, error number - {ERROR_NUM}",
307             "TYPE", certType, "ERROR_NUM", errno);
308         return PLDM_ERROR;
309     }
310     certMap.emplace(certType, std::tuple(fileFd, length));
311     return PLDM_SUCCESS;
312 }
313 
314 int CertHandler::fileAckWithMetaData(uint8_t fileStatus,
315                                      uint32_t /*metaDataValue1*/,
316                                      uint32_t /*metaDataValue2*/,
317                                      uint32_t /*metaDataValue3*/,
318                                      uint32_t /*metaDataValue4*/)
319 {
320     if (certType == PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
321     {
322         DBusMapping dbusMapping{certObjPath + std::to_string(fileHandle),
323                                 certEntryIntf, "Status", "string"};
324         PropertyValue value = "xyz.openbmc_project.Certs.Entry.State.Pending";
325         if (fileStatus == PLDM_ERROR_INVALID_DATA)
326         {
327             value = "xyz.openbmc_project.Certs.Entry.State.BadCSR";
328         }
329         else if (fileStatus == PLDM_ERROR_NOT_READY)
330         {
331             value = "xyz.openbmc_project.Certs.Entry.State.Pending";
332         }
333         try
334         {
335             pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
336         }
337         catch (const std::exception& e)
338         {
339             error(
340                 "Failed to set status property of certificate entry for file ack with meta data, error - {ERROR}",
341                 "ERROR", e);
342             return PLDM_ERROR;
343         }
344     }
345     return PLDM_SUCCESS;
346 }
347 
348 } // namespace responder
349 } // namespace pldm
350