PLDM : Multiple vmi certificate exchange

With this story PLDM detects interface added signal on interface
xyz.openbmc_project.Certs.Entry and saves the value of CSR property.
This CSR string is then sent to the host. Once the host responds to it
the response is verified and a client certificate received from host
is updates/saved in a dbus property - ClientCertificate.

For multiple certificate exchange, new dbus objects are
created for signing requests from different clients. Each dbus
object has properties such has CSR and Client certificate that get
updated with the certificate string when a valid CSR request is
sent to host and client certficate is received successfully.

After the dbus property (ClientCertificate) has a valid client
certificate string, status property of the dbus interface
xyz.openbmc_project.Certs.Entry is updated from pending to complete.

Signed-off-by: Varsha Kaverappa <vkaverap@in.ibm.com>
Change-Id: I63afb15190ae9c21eb86421d75f51618b358c074
diff --git a/oem/ibm/libpldmresponder/file_io.hpp b/oem/ibm/libpldmresponder/file_io.hpp
index a79c677..430811e 100644
--- a/oem/ibm/libpldmresponder/file_io.hpp
+++ b/oem/ibm/libpldmresponder/file_io.hpp
@@ -158,6 +158,10 @@
 {
 static constexpr auto dumpObjPath = "/xyz/openbmc_project/dump/resource/entry/";
 static constexpr auto resDumpEntry = "com.ibm.Dump.Entry.Resource";
+
+static constexpr auto certObjPath = "/xyz/openbmc_project/certs/ca/";
+static constexpr auto certAuthority =
+    "xyz.openbmc_project.PLDM.Provider.Certs.Authority.CSR";
 class Handler : public CmdHandler
 {
   public:
@@ -264,6 +268,47 @@
                     }
                 }
             });
+        vmiCertMatcher = std::make_unique<sdbusplus::bus::match::match>(
+            pldm::utils::DBusHandler::getBus(),
+            sdbusplus::bus::match::rules::interfacesAdded() +
+                sdbusplus::bus::match::rules::argNpath(0, certObjPath),
+            [hostSockFd, hostEid,
+             dbusImplReqester](sdbusplus::message::message& msg) {
+                std::map<
+                    std::string,
+                    std::map<std::string, std::variant<std::string, uint32_t>>>
+                    interfaces;
+                sdbusplus::message::object_path path;
+                msg.read(path, interfaces);
+                std::string csr;
+
+                for (auto& interface : interfaces)
+                {
+                    if (interface.first == certAuthority)
+                    {
+                        for (const auto& property : interface.second)
+                        {
+                            if (property.first == "CSR")
+                            {
+                                csr = std::get<std::string>(property.second);
+                                auto fileHandle =
+                                    sdbusplus::message::object_path(path)
+                                        .filename();
+
+                                auto dbusToFileHandler =
+                                    std::make_unique<pldm::requester::oem_ibm::
+                                                         DbusToFileHandler>(
+                                        hostSockFd, hostEid, dbusImplReqester,
+                                        path);
+                                dbusToFileHandler->newCsrFileAvailable(
+                                    csr, fileHandle);
+                                break;
+                            }
+                        }
+                        break;
+                    }
+                }
+            });
     }
 
     /** @brief Handler for readFileIntoMemory command
@@ -374,6 +419,9 @@
     std::unique_ptr<sdbusplus::bus::match::match>
         resDumpMatcher; //!< Pointer to capture the interface added signal
                         //!< for new resource dump
+    std::unique_ptr<sdbusplus::bus::match::match>
+        vmiCertMatcher; //!< Pointer to capture the interface added signal
+                        //!< for new csr string
 };
 
 } // namespace oem_ibm
diff --git a/oem/ibm/libpldmresponder/file_io_type_cert.cpp b/oem/ibm/libpldmresponder/file_io_type_cert.cpp
index f21cdf2..3fe123e 100644
--- a/oem/ibm/libpldmresponder/file_io_type_cert.cpp
+++ b/oem/ibm/libpldmresponder/file_io_type_cert.cpp
@@ -14,9 +14,7 @@
 namespace responder
 {
 
-static constexpr auto csrFilePath = "/var/lib/bmcweb/CSR";
-static constexpr auto rootCertPath = "/var/lib/bmcweb/RootCert";
-static constexpr auto clientCertPath = "/var/lib/bmcweb/ClientCert";
+static constexpr auto certFilePath = "/var/lib/ibm/bmcweb/";
 
 CertMap CertHandler::certMap;
 
@@ -50,21 +48,37 @@
                                 uint64_t address,
                                 oem_platform::Handler* /*oemPlatformHandler*/)
 {
+    std::string filePath = certFilePath;
+    filePath += "CSR_" + std::to_string(fileHandle);
     if (certType != PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
     {
         return PLDM_ERROR_INVALID_DATA;
     }
-    return transferFileData(csrFilePath, true, offset, length, address);
+    auto rc = transferFileData(filePath.c_str(), true, offset, length, address);
+    fs::remove(filePath);
+    if (rc)
+    {
+        return PLDM_ERROR;
+    }
+    return PLDM_SUCCESS;
 }
 
 int CertHandler::read(uint32_t offset, uint32_t& length, Response& response,
                       oem_platform::Handler* /*oemPlatformHandler*/)
 {
+    std::string filePath = certFilePath;
+    filePath += "CSR_" + std::to_string(fileHandle);
     if (certType != PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
     {
         return PLDM_ERROR_INVALID_DATA;
     }
-    return readFile(csrFilePath, offset, length, response);
+    auto rc = readFile(filePath.c_str(), offset, length, response);
+    fs::remove(filePath);
+    if (rc)
+    {
+        return PLDM_ERROR;
+    }
+    return PLDM_SUCCESS;
 }
 
 int CertHandler::write(const char* buffer, uint32_t offset, uint32_t& length,
@@ -100,15 +114,90 @@
         close(fd);
         certMap.erase(it);
     }
+
+    if (certType == PLDM_FILE_TYPE_SIGNED_CERT)
+    {
+        constexpr auto certObjPath = "/xyz/openbmc_project/certs/ca/entry/";
+        constexpr auto certEntryIntf = "xyz.openbmc_project.Certs.Entry";
+
+        std::string filePath = certFilePath;
+        filePath += "ClientCert_" + std::to_string(fileHandle);
+
+        std::ifstream inFile;
+        inFile.open(filePath);
+        std::stringstream strStream;
+        strStream << inFile.rdbuf();
+        std::string str = strStream.str();
+        inFile.close();
+
+        if (!str.empty())
+        {
+            PropertyValue value{str};
+
+            DBusMapping dbusMapping{certObjPath + std::to_string(fileHandle),
+                                    certEntryIntf, "ClientCertificate",
+                                    "string"};
+            try
+            {
+                pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
+            }
+            catch (const std::exception& e)
+            {
+                std::cerr << "failed to set Client certificate, "
+                             "ERROR="
+                          << e.what() << "\n";
+                return PLDM_ERROR;
+            }
+            PropertyValue valueStatus{
+                "xyz.openbmc_project.Certs.Entry.State.Complete"};
+            DBusMapping dbusMappingStatus{certObjPath +
+                                              std::to_string(fileHandle),
+                                          certEntryIntf, "Status", "string"};
+            try
+            {
+                pldm::utils::DBusHandler().setDbusProperty(dbusMappingStatus,
+                                                           valueStatus);
+            }
+            catch (const std::exception& e)
+            {
+                std::cerr
+                    << "failed to set status property of certicate entry, "
+                       "ERROR="
+                    << e.what() << "\n";
+                return PLDM_ERROR;
+            }
+            fs::remove(filePath);
+        }
+        else
+        {
+            PropertyValue value{"xyz.openbmc_project.Certs.Entry.State.BadCSR"};
+            DBusMapping dbusMapping{certObjPath + std::to_string(fileHandle),
+                                    certEntryIntf, "Status", "string"};
+            try
+            {
+                pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
+            }
+            catch (const std::exception& e)
+            {
+                std::cerr
+                    << "failed to set status property of certicate entry, "
+                       "ERROR="
+                    << e.what() << "\n";
+                return PLDM_ERROR;
+            }
+        }
+    }
     return PLDM_SUCCESS;
 }
 
 int CertHandler::newFileAvailable(uint64_t length)
 {
-    static constexpr auto vmiCertPath = "/var/lib/bmcweb";
-    fs::create_directories(vmiCertPath);
+    fs::create_directories(certFilePath);
+    fs::permissions(certFilePath,
+                    fs::perms::others_read | fs::perms::owner_write);
     int fileFd = -1;
     int flags = O_WRONLY | O_CREAT | O_TRUNC;
+    std::string filePath = certFilePath;
 
     if (certType == PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
     {
@@ -116,11 +205,14 @@
     }
     if (certType == PLDM_FILE_TYPE_SIGNED_CERT)
     {
-        fileFd = open(clientCertPath, flags, S_IRUSR | S_IWUSR);
+        fileFd = open(
+            (filePath + "ClientCert_" + std::to_string(fileHandle)).c_str(),
+            flags, S_IRUSR | S_IWUSR);
     }
     else if (certType == PLDM_FILE_TYPE_ROOT_CERT)
     {
-        fileFd = open(rootCertPath, flags, S_IRUSR | S_IWUSR);
+        fileFd =
+            open((filePath + "RootCert").c_str(), flags, S_IRUSR | S_IWUSR);
     }
     if (fileFd == -1)
     {
diff --git a/oem/ibm/requester/dbus_to_file_handler.cpp b/oem/ibm/requester/dbus_to_file_handler.cpp
index 754b901..e256bc8 100644
--- a/oem/ibm/requester/dbus_to_file_handler.cpp
+++ b/oem/ibm/requester/dbus_to_file_handler.cpp
@@ -190,6 +190,112 @@
     sendNewFileAvailableCmd(fileSize);
 }
 
+void DbusToFileHandler::newCsrFileAvailable(const std::string& csr,
+                                            const std::string fileHandle)
+{
+    namespace fs = std::filesystem;
+    std::string dirPath = "/var/lib/ibm/bmcweb";
+    const fs::path certDirPath = dirPath;
+
+    if (!fs::exists(certDirPath))
+    {
+        fs::create_directories(certDirPath);
+        fs::permissions(certDirPath,
+                        fs::perms::others_read | fs::perms::owner_write);
+    }
+
+    fs::path certFilePath = certDirPath / ("CSR_" + fileHandle);
+    std::ofstream certFile;
+
+    certFile.open(certFilePath, std::ios::out | std::ofstream::binary);
+
+    if (!certFile)
+    {
+        std::cerr << "cert file open error: " << certFilePath << "\n";
+        return;
+    }
+
+    // Add csr to file
+    certFile << csr << std::endl;
+
+    certFile.close();
+    uint32_t fileSize = fs::file_size(certFilePath);
+
+    newFileAvailableSendToHost(fileSize, (uint32_t)stoi(fileHandle),
+                               PLDM_FILE_TYPE_CERT_SIGNING_REQUEST);
+}
+
+void DbusToFileHandler::newFileAvailableSendToHost(const uint32_t fileSize,
+                                                   const uint32_t fileHandle,
+                                                   const uint16_t type)
+{
+    if (requester == NULL)
+    {
+        std::cerr << "Failed to send csr to host.";
+        pldm::utils::reportError(
+            "xyz.openbmc_project.bmc.pldm.InternalFailure");
+        return;
+    }
+    auto instanceId = requester->getInstanceId(mctp_eid);
+    std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
+                                    PLDM_NEW_FILE_REQ_BYTES);
+    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+
+    auto rc =
+        encode_new_file_req(instanceId, type, fileHandle, fileSize, request);
+    if (rc != PLDM_SUCCESS)
+    {
+        requester->markFree(mctp_eid, instanceId);
+        std::cerr << "Failed to encode_new_file_req, rc = " << rc << std::endl;
+        return;
+    }
+
+    uint8_t* responseMsg = nullptr;
+    size_t responseMsgSize{};
+
+    auto requesterRc =
+        pldm_send_recv(mctp_eid, mctp_fd, requestMsg.data(), requestMsg.size(),
+                       &responseMsg, &responseMsgSize);
+
+    std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{responseMsg,
+                                                                  std::free};
+
+    requester->markFree(mctp_eid, instanceId);
+    bool isDecodeNewFileRespFailed = false;
+    if (requesterRc != PLDM_REQUESTER_SUCCESS)
+    {
+        std::cerr << "Failed to send file to host, rc = " << requesterRc
+                  << std::endl;
+    }
+    else
+    {
+        uint8_t completionCode{};
+        auto responsePtr =
+            reinterpret_cast<struct pldm_msg*>(responseMsgPtr.get());
+
+        rc = decode_new_file_resp(responsePtr, PLDM_NEW_FILE_RESP_BYTES,
+                                  &completionCode);
+
+        std::vector<uint8_t> responseMsgVec;
+        responseMsgVec.resize(responseMsgSize);
+        memcpy(responseMsgVec.data(), responseMsg, responseMsgVec.size());
+
+        if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
+        {
+            std::cerr << "Failed to decode_new_file_resp: "
+                      << "rc=" << rc
+                      << ", cc=" << static_cast<unsigned>(completionCode)
+                      << std::endl;
+            isDecodeNewFileRespFailed = true;
+        }
+    }
+    if ((requesterRc != PLDM_REQUESTER_SUCCESS) || (isDecodeNewFileRespFailed))
+    {
+        pldm::utils::reportError(
+            "xyz.openbmc_project.bmc.pldm.InternalFailure");
+    }
+}
+
 } // namespace oem_ibm
 } // namespace requester
 } // namespace pldm
diff --git a/oem/ibm/requester/dbus_to_file_handler.hpp b/oem/ibm/requester/dbus_to_file_handler.hpp
index b6e8b5d..bd7b3c6 100644
--- a/oem/ibm/requester/dbus_to_file_handler.hpp
+++ b/oem/ibm/requester/dbus_to_file_handler.hpp
@@ -48,12 +48,28 @@
     void processNewResourceDump(const std::string& vspString,
                                 const std::string& resDumpReqPass);
 
+    /** @brief Process the new CSR file available
+     *  @param[in] csr - CSR string
+     *  @param[in] fileHandle - file Handle for  new file request
+     */
+    void newCsrFileAvailable(const std::string& csr,
+                             const std::string fileHandle);
+
   private:
     /** @brief Send the new file available command request to hypervisor
      *  @param[in] fileSize - size of the file
      */
     void sendNewFileAvailableCmd(uint64_t fileSize);
 
+    /** @brief Send the new file available command request to hypervisor
+     *  @param[in] fileSize - size of the file
+     *  @param[in] fileHandle - file handle
+     *  @param[in] type - file type
+     */
+    void newFileAvailableSendToHost(const uint32_t fileSize,
+                                    const uint32_t fileHandle,
+                                    const uint16_t type);
+
     /** @brief fd of MCTP communications socket */
     int mctp_fd;