pldm oem: implement certificate handler

This commit adds code to handle the ceritificate request and
certificate transfer between the host and bmc following pldm oem
file i/o protocol

Change-Id: I8095e0f5ad8c3c5cc796da1cfbe4bb9946af31cf
Signed-off-by: Sampa Misra <sampmisr@in.ibm.com>
diff --git a/libpldmresponder/meson.build b/libpldmresponder/meson.build
index c178269..bb0d10c 100644
--- a/libpldmresponder/meson.build
+++ b/libpldmresponder/meson.build
@@ -27,7 +27,8 @@
     '../oem/ibm/libpldmresponder/file_table.cpp',
     '../oem/ibm/libpldmresponder/file_io_by_type.cpp',
     '../oem/ibm/libpldmresponder/file_io_type_pel.cpp',
-    '../oem/ibm/libpldmresponder/file_io_type_dump.cpp'
+    '../oem/ibm/libpldmresponder/file_io_type_dump.cpp',
+    '../oem/ibm/libpldmresponder/file_io_type_cert.cpp'
   ]
 endif
 
diff --git a/oem/ibm/libpldm/file_io.c b/oem/ibm/libpldm/file_io.c
index 16b56e6..69aed29 100644
--- a/oem/ibm/libpldm/file_io.c
+++ b/oem/ibm/libpldm/file_io.c
@@ -598,7 +598,7 @@
 		return PLDM_ERROR_INVALID_DATA;
 	}
 
-	if (payload_length != PLDM_RW_FILE_BY_TYPE_REQ_BYTES) {
+	if (payload_length < PLDM_RW_FILE_BY_TYPE_REQ_BYTES) {
 		return PLDM_ERROR_INVALID_LENGTH;
 	}
 
diff --git a/oem/ibm/libpldm/file_io.h b/oem/ibm/libpldm/file_io.h
index 53f4343..aebe315 100644
--- a/oem/ibm/libpldm/file_io.h
+++ b/oem/ibm/libpldm/file_io.h
@@ -52,6 +52,9 @@
 	PLDM_FILE_TYPE_LID_PERM = 1,
 	PLDM_FILE_TYPE_LID_TEMP = 2,
 	PLDM_FILE_TYPE_DUMP = 3,
+	PLDM_FILE_TYPE_CERT_SIGNING_REQUEST = 4,
+	PLDM_FILE_TYPE_SIGNED_CERT = 5,
+	PLDM_FILE_TYPE_ROOT_CERT = 6,
 };
 
 #define PLDM_RW_FILE_MEM_REQ_BYTES 20
diff --git a/oem/ibm/libpldmresponder/file_io.cpp b/oem/ibm/libpldmresponder/file_io.cpp
index 9322d9d..ff7f0ee 100644
--- a/oem/ibm/libpldmresponder/file_io.cpp
+++ b/oem/ibm/libpldmresponder/file_io.cpp
@@ -616,7 +616,7 @@
     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_BY_TYPE_RESP_BYTES);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
 
-    if (payloadLength != PLDM_RW_FILE_BY_TYPE_REQ_BYTES)
+    if (payloadLength < PLDM_RW_FILE_BY_TYPE_REQ_BYTES)
     {
         encode_rw_file_by_type_resp(request->hdr.instance_id,
                                     PLDM_WRITE_FILE_BY_TYPE,
@@ -653,7 +653,7 @@
     }
 
     rc = handler->write(reinterpret_cast<const char*>(
-                            request->payload + PLDM_RW_FILE_BY_TYPE_RESP_BYTES),
+                            request->payload + PLDM_RW_FILE_BY_TYPE_REQ_BYTES),
                         offset, length);
     encode_rw_file_by_type_resp(request->hdr.instance_id,
                                 PLDM_WRITE_FILE_BY_TYPE, rc, length,
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.cpp b/oem/ibm/libpldmresponder/file_io_by_type.cpp
index 2ecc3d9..540937c 100644
--- a/oem/ibm/libpldmresponder/file_io_by_type.cpp
+++ b/oem/ibm/libpldmresponder/file_io_by_type.cpp
@@ -2,6 +2,7 @@
 
 #include "file_io_by_type.hpp"
 
+#include "file_io_type_cert.hpp"
 #include "file_io_type_dump.hpp"
 #include "file_io_type_lid.hpp"
 #include "file_io_type_pel.hpp"
@@ -126,6 +127,13 @@
             return std::make_unique<DumpHandler>(fileHandle);
             break;
         }
+        case PLDM_FILE_TYPE_CERT_SIGNING_REQUEST:
+        case PLDM_FILE_TYPE_SIGNED_CERT:
+        case PLDM_FILE_TYPE_ROOT_CERT:
+        {
+            return std::make_unique<CertHandler>(fileHandle, fileType);
+            break;
+        }
         default:
         {
             throw InternalFailure();
diff --git a/oem/ibm/libpldmresponder/file_io_type_cert.cpp b/oem/ibm/libpldmresponder/file_io_type_cert.cpp
new file mode 100644
index 0000000..aef3f4c
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_io_type_cert.cpp
@@ -0,0 +1,132 @@
+#include "file_io_type_cert.hpp"
+
+#include "utils.hpp"
+
+#include <stdint.h>
+
+#include <iostream>
+
+#include "libpldm/base.h"
+#include "oem/ibm/libpldm/file_io.h"
+
+namespace pldm
+{
+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";
+
+CertMap CertHandler::certMap;
+
+int CertHandler::writeFromMemory(uint32_t offset, uint32_t length,
+                                 uint64_t address)
+{
+    auto it = certMap.find(certType);
+    if (it == certMap.end())
+    {
+        std::cerr << "file for type " << certType << " doesn't exist\n";
+        return PLDM_ERROR;
+    }
+
+    auto fd = std::get<0>(it->second);
+    auto& remSize = std::get<1>(it->second);
+    auto rc = transferFileData(fd, false, offset, length, address);
+    if (rc == PLDM_SUCCESS)
+    {
+        remSize -= length;
+        if (!remSize)
+        {
+            close(fd);
+            certMap.erase(it);
+        }
+    }
+    return rc;
+}
+
+int CertHandler::readIntoMemory(uint32_t offset, uint32_t& length,
+                                uint64_t address)
+{
+    if (certType != PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
+    {
+        return PLDM_ERROR_INVALID_DATA;
+    }
+    return transferFileData(csrFilePath, true, offset, length, address);
+}
+
+int CertHandler::read(uint32_t offset, uint32_t& length, Response& response)
+{
+    if (certType != PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
+    {
+        return PLDM_ERROR_INVALID_DATA;
+    }
+    return readFile(csrFilePath, offset, length, response);
+}
+
+int CertHandler::write(const char* buffer, uint32_t offset, uint32_t& length)
+{
+    auto it = certMap.find(certType);
+    if (it == certMap.end())
+    {
+        std::cerr << "file for type " << certType << " doesn't exist\n";
+        return PLDM_ERROR;
+    }
+
+    auto fd = std::get<0>(it->second);
+    int rc = lseek(fd, offset, SEEK_SET);
+    if (rc == -1)
+    {
+        std::cerr << "lseek failed, ERROR=" << errno << ", OFFSET=" << offset
+                  << "\n";
+        return PLDM_ERROR;
+    }
+    rc = ::write(fd, buffer, length);
+    if (rc == -1)
+    {
+        std::cerr << "file write failed, ERROR=" << errno
+                  << ", LENGTH=" << length << ", OFFSET=" << offset << "\n";
+        return PLDM_ERROR;
+    }
+    length = rc;
+    auto& remSize = std::get<1>(it->second);
+    remSize -= length;
+    if (!remSize)
+    {
+        close(fd);
+        certMap.erase(it);
+    }
+    return PLDM_SUCCESS;
+}
+
+int CertHandler::newFileAvailable(uint64_t length)
+{
+    static constexpr auto vmiCertPath = "/var/lib/bmcweb";
+    fs::create_directories(vmiCertPath);
+    int fileFd = -1;
+    int flags = O_WRONLY | O_CREAT | O_TRUNC;
+
+    if (certType == PLDM_FILE_TYPE_CERT_SIGNING_REQUEST)
+    {
+        return PLDM_ERROR_INVALID_DATA;
+    }
+    if (certType == PLDM_FILE_TYPE_SIGNED_CERT)
+    {
+        fileFd = open(clientCertPath, flags);
+    }
+    else if (certType == PLDM_FILE_TYPE_ROOT_CERT)
+    {
+        fileFd = open(rootCertPath, flags);
+    }
+    if (fileFd == -1)
+    {
+        std::cerr << "failed to open file for type " << certType
+                  << " ERROR=" << errno << "\n";
+        return PLDM_ERROR;
+    }
+    certMap.emplace(certType, std::tuple(fileFd, length));
+    return PLDM_SUCCESS;
+}
+
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/file_io_type_cert.hpp b/oem/ibm/libpldmresponder/file_io_type_cert.hpp
new file mode 100644
index 0000000..a981fab
--- /dev/null
+++ b/oem/ibm/libpldmresponder/file_io_type_cert.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "file_io_by_type.hpp"
+
+#include <tuple>
+
+namespace pldm
+{
+namespace responder
+{
+
+using Fd = int;
+using RemainingSize = uint64_t;
+using CertDetails = std::tuple<Fd, RemainingSize>;
+using CertType = uint16_t;
+using CertMap = std::map<CertType, CertDetails>;
+
+/** @class CertHandler
+ *
+ *  @brief Inherits and implements FileHandler. This class is used
+ *  to read/write certificates and certificate signing requests
+ */
+class CertHandler : public FileHandler
+{
+  public:
+    /** @brief CertHandler constructor
+     */
+    CertHandler(uint32_t fileHandle, uint16_t fileType) :
+        FileHandler(fileHandle), certType(fileType)
+    {
+    }
+
+    virtual int writeFromMemory(uint32_t offset, uint32_t length,
+                                uint64_t address);
+    virtual int readIntoMemory(uint32_t offset, uint32_t& length,
+                               uint64_t address);
+    virtual int read(uint32_t offset, uint32_t& length, Response& response);
+
+    virtual int write(const char* buffer, uint32_t offset, uint32_t& length);
+
+    virtual int fileAck(uint8_t /*fileStatus*/)
+    {
+        return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+    }
+
+    virtual int newFileAvailable(uint64_t length);
+
+    /** @brief CertHandler destructor
+     */
+    ~CertHandler()
+    {
+    }
+
+  private:
+    uint16_t certType;      //!< type of the certificate
+    static CertMap certMap; //!< holds the fd and remaining read/write size for
+                            //!< each certificate
+};
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/test/libpldmresponder_fileio_test.cpp b/oem/ibm/test/libpldmresponder_fileio_test.cpp
index a8d0722..c19d769 100644
--- a/oem/ibm/test/libpldmresponder_fileio_test.cpp
+++ b/oem/ibm/test/libpldmresponder_fileio_test.cpp
@@ -1,5 +1,6 @@
 #include "libpldmresponder/file_io.hpp"
 #include "libpldmresponder/file_io_by_type.hpp"
+#include "libpldmresponder/file_io_type_cert.hpp"
 #include "libpldmresponder/file_io_type_dump.hpp"
 #include "libpldmresponder/file_io_type_lid.hpp"
 #include "libpldmresponder/file_io_type_pel.hpp"
@@ -800,6 +801,18 @@
     auto dumpType = dynamic_cast<DumpHandler*>(handler.get());
     ASSERT_TRUE(dumpType != nullptr);
 
+    handler = getHandlerByType(PLDM_FILE_TYPE_CERT_SIGNING_REQUEST, fileHandle);
+    auto certType = dynamic_cast<CertHandler*>(handler.get());
+    ASSERT_TRUE(certType != nullptr);
+
+    handler = getHandlerByType(PLDM_FILE_TYPE_SIGNED_CERT, fileHandle);
+    certType = dynamic_cast<CertHandler*>(handler.get());
+    ASSERT_TRUE(certType != nullptr);
+
+    handler = getHandlerByType(PLDM_FILE_TYPE_ROOT_CERT, fileHandle);
+    certType = dynamic_cast<CertHandler*>(handler.get());
+    ASSERT_TRUE(certType != nullptr);
+
     using namespace sdbusplus::xyz::openbmc_project::Common::Error;
     ASSERT_THROW(getHandlerByType(0xFFFF, fileHandle), InternalFailure);
 }