PNOR Signature validation failure handling based on field mode

Added support to stop the codeupdate only for the fieldmode
enabled systems, for signature validation failures.

Resolves openbmc/openbmc#3047

Change-Id: Idf47b122a60d5d14e6e7f134d8067d20e09e7c76
Signed-off-by: Jayanth Othayoth <ojayanth@in.ibm.com>
diff --git a/activation.cpp b/activation.cpp
index b7cdb51..87e5d2b 100755
--- a/activation.cpp
+++ b/activation.cpp
@@ -6,11 +6,11 @@
 #include <phosphor-logging/log.hpp>
 
 #ifdef WANT_SIGNATURE_VERIFY
+#include <sdbusplus/server.hpp>
 #include <phosphor-logging/elog.hpp>
 #include <phosphor-logging/elog-errors.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
 #include "image_verify.hpp"
-#include "config.h"
 #endif
 
 namespace openpower
@@ -28,6 +28,10 @@
 #ifdef WANT_SIGNATURE_VERIFY
 using InternalFailure =
     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+// Field mode path and interface.
+constexpr auto FIELDMODE_PATH("/xyz/openbmc_project/software");
+constexpr auto FIELDMODE_INTERFACE("xyz.openbmc_project.Control.FieldMode");
 #endif
 
 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
@@ -120,24 +124,17 @@
         {
 
 #ifdef WANT_SIGNATURE_VERIFY
-            using Signature = openpower::software::image::Signature;
-
-            fs::path imagePath(IMG_DIR);
-
-            Signature signature(imagePath / versionId,
-                                PNOR_SIGNED_IMAGE_CONF_PATH);
-
             // Validate the signed image.
-            if (!signature.verify())
+            if (!validateSignature())
             {
-                log<level::ERR>("Error occurred during image validation");
-                report<InternalFailure>();
+                // Cleanup
+                activationBlocksTransition.reset(nullptr);
+                activationProgress.reset(nullptr);
 
                 return softwareServer::Activation::activation(
                     softwareServer::Activation::Activations::Failed);
             }
 #endif
-
             Activation::startActivation();
             return softwareServer::Activation::activation(value);
         }
@@ -276,6 +273,93 @@
     return;
 }
 
+#ifdef WANT_SIGNATURE_VERIFY
+inline bool Activation::validateSignature()
+{
+    using Signature = openpower::software::image::Signature;
+    fs::path imageDir(IMG_DIR);
+
+    Signature signature(imageDir / versionId, PNOR_SIGNED_IMAGE_CONF_PATH);
+
+    // Validate the signed image.
+    if (signature.verify())
+    {
+        return true;
+    }
+    // Log error and continue activation process, if field mode disabled.
+    log<level::ERR>("Error occurred during image validation");
+    report<InternalFailure>();
+
+    try
+    {
+        if (!fieldModeEnabled())
+        {
+            return true;
+        }
+    }
+    catch (const InternalFailure& e)
+    {
+        report<InternalFailure>();
+    }
+    return false;
+}
+
+bool Activation::fieldModeEnabled()
+{
+    auto fieldModeSvc = getService(bus, FIELDMODE_PATH, FIELDMODE_INTERFACE);
+
+    auto method = bus.new_method_call(fieldModeSvc.c_str(), FIELDMODE_PATH,
+                                      "org.freedesktop.DBus.Properties", "Get");
+
+    method.append(FIELDMODE_INTERFACE, "FieldModeEnabled");
+    auto reply = bus.call(method);
+    if (reply.is_method_error())
+    {
+        log<level::ERR>("Error in fieldModeEnabled getValue");
+        elog<InternalFailure>();
+    }
+    sdbusplus::message::variant<bool> fieldMode;
+    reply.read(fieldMode);
+
+    return (fieldMode.get<bool>());
+}
+
+std::string Activation::getService(sdbusplus::bus::bus& bus,
+                                   const std::string& path,
+                                   const std::string& intf)
+{
+    auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
+                                          MAPPER_INTERFACE, "GetObject");
+
+    mapperCall.append(path);
+    mapperCall.append(std::vector<std::string>({intf}));
+
+    auto mapperResponseMsg = bus.call(mapperCall);
+
+    if (mapperResponseMsg.is_method_error())
+    {
+        log<level::ERR>("ERROR in getting service",
+                        entry("PATH=%s", path.c_str()),
+                        entry("INTERFACE=%s", intf.c_str()));
+
+        elog<InternalFailure>();
+    }
+
+    std::map<std::string, std::vector<std::string>> mapperResponse;
+    mapperResponseMsg.read(mapperResponse);
+
+    if (mapperResponse.begin() == mapperResponse.end())
+    {
+        log<level::ERR>("ERROR reading mapper response",
+                        entry("PATH=%s", path.c_str()),
+                        entry("INTERFACE=%s", intf.c_str()));
+
+        elog<InternalFailure>();
+    }
+    return mapperResponse.begin()->first;
+}
+#endif
+
 } // namespace updater
 } // namespace software
 } // namespace openpower
diff --git a/activation.hpp b/activation.hpp
index 2edbca5..1a600cc 100755
--- a/activation.hpp
+++ b/activation.hpp
@@ -7,6 +7,7 @@
 #include "xyz/openbmc_project/Software/RedundancyPriority/server.hpp"
 #include "xyz/openbmc_project/Software/ActivationProgress/server.hpp"
 #include "org/openbmc/Associations/server.hpp"
+#include "config.h"
 
 namespace openpower
 {
@@ -300,6 +301,43 @@
 
     /** @brief Member function for clarity & brevity at activation end */
     void finishActivation();
+
+#ifdef WANT_SIGNATURE_VERIFY
+    /**
+     * @brief Wrapper function for the signature verify function.
+     *        Signature class verify function used for validating
+     *        signed image. Also added additional logic to continue
+     *        update process in lab environment by checking the
+     *        fieldModeEnabled property.
+     *
+     * @return  true if successful signature validation or field
+     *          mode is disabled.
+     *          false for unsuccessful signature validation or
+     *          any internal failure during the mapper call.
+     */
+    inline bool validateSignature();
+
+    /**
+     * @brief Gets the fieldModeEnabled property value.
+     *
+     * @return fieldModeEnabled property value
+     * @error  InternalFailure exception thrown
+     */
+    bool fieldModeEnabled();
+
+    /**
+     * @brief Gets the D-Bus Service name for the input D-Bus path
+     *
+     * @param[in] bus  -  Bus handler
+     * @param[in] path -  Object Path
+     * @param[in] intf -  Interface
+     *
+     * @return  Service name
+     * @error   InternalFailure exception thrown
+     */
+    std::string getService(sdbusplus::bus::bus& bus, const std::string& path,
+                           const std::string& intf);
+#endif
 };
 
 } // namespace updater