Manage certificates created by applications

Added watch on certificate path to watch on certificates
created/updated by apps.

As part of watch notification, create new D-Bus new certificate
and for existing D-Bus object update the properties.

Tested:
Test case 1
1) Ensure no certificate is present
2) Restart certificate service
3) Restart bmcweb service
4) Verified that certificate object is created for the
   self-signed certificate created by bmcweb.

Test case 2
1) After a certificate is present
2) Modify the bmcweb certificate by replacing it
   with a valid certificate manually.
3) Verified that certificate manager is notified
and certificate objects properties are updated.

Test case 3
1) Upload CSR based certificate file
2) Verified that private key is appended to the file

Test case 4
1) Create a dummy file in certificate folder
2) Verified that notification is received and file is ignored

Test case 5
1) Verified install, replace, generate csr.

Change-Id: I7d1e3624958e4b68e5ba7bc6150c19b11fca501a
Signed-off-by: Marri Devender Rao <devenrao@in.ibm.com>
diff --git a/certs_manager.cpp b/certs_manager.cpp
index 38f77c2..b189bb9 100644
--- a/certs_manager.cpp
+++ b/certs_manager.cpp
@@ -12,6 +12,9 @@
 {
 using InternalFailure =
     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using InvalidCertificate =
+    sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
+using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
 
 using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
 using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
@@ -21,33 +24,45 @@
                  UnitsToRestart&& unit, CertInstallPath&& installPath) :
     Ifaces(bus, path),
     bus(bus), event(event), objectPath(path), certType(type),
-    unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
-    childPtr(nullptr)
+    unitToRestart(std::move(unit)), certInstallPath(std::move(installPath))
 {
-    using InvalidCertificate =
-        sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
-    using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
+    // restore any existing certificates
     if (fs::exists(certInstallPath))
     {
-        try
-        {
-            // TODO: Issue#3 At present supporting only one certificate to be
-            // uploaded this need to be revisited to support multiple
-            // certificates
-            auto certObjectPath = objectPath + '/' + '1';
-            certificatePtr = std::make_unique<Certificate>(
-                bus, certObjectPath, certType, unitToRestart, certInstallPath,
-                certInstallPath, true);
-        }
-        catch (const InternalFailure& e)
-        {
-            report<InternalFailure>();
-        }
-        catch (const InvalidCertificate& e)
-        {
-            report<InvalidCertificate>(
-                Reason("Existing certificate file is corrupted"));
-        }
+        createCertificate();
+    }
+
+    // watch is not required for authority certificates
+    if (certType != AUTHORITY)
+    {
+        // watch for certificate file create/replace
+        certWatchPtr = std::make_unique<
+            Watch>(event, certInstallPath, [this]() {
+            try
+            {
+                // if certificate file existing update it
+                if (certificatePtr != nullptr)
+                {
+                    log<level::INFO>(
+                        "Inotify callback to update certificate properties");
+                    certificatePtr->populateProperties();
+                }
+                else
+                {
+                    log<level::INFO>(
+                        "Inotify callback to create certificate object");
+                    createCertificate();
+                }
+            }
+            catch (const InternalFailure& e)
+            {
+                commit<InternalFailure>();
+            }
+            catch (const InvalidCertificate& e)
+            {
+                commit<InvalidCertificate>();
+            }
+        });
     }
 }
 
@@ -63,10 +78,11 @@
     {
         elog<NotAllowed>(Reason("Certificate already exist"));
     }
+
     auto certObjectPath = objectPath + '/' + '1';
     certificatePtr = std::make_unique<Certificate>(
         bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath,
-        false);
+        false, certWatchPtr);
 }
 
 void Manager::delete_()
@@ -170,6 +186,11 @@
     return csrObjectPath;
 }
 
+CertificatePtr& Manager::getCertificate()
+{
+    return certificatePtr;
+}
+
 void Manager::generateCSRHelper(
     std::vector<std::string> alternativeNames, std::string challengePassword,
     std::string city, std::string commonName, std::string contactPerson,
@@ -460,5 +481,27 @@
     std::fclose(fp);
 }
 
+void Manager::createCertificate()
+{
+    try
+    {
+        // TODO: Issue#3 At present supporting only one certificate to be
+        // uploaded this need to be revisited to support multiple
+        // certificates
+        auto certObjectPath = objectPath + '/' + '1';
+        certificatePtr = std::make_unique<Certificate>(
+            bus, certObjectPath, certType, unitToRestart, certInstallPath,
+            certInstallPath, true, certWatchPtr);
+    }
+    catch (const InternalFailure& e)
+    {
+        report<InternalFailure>();
+    }
+    catch (const InvalidCertificate& e)
+    {
+        report<InvalidCertificate>(
+            Reason("Existing certificate file is corrupted"));
+    }
+}
 } // namespace certs
 } // namespace phosphor