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/watch.cpp b/watch.cpp
new file mode 100644
index 0000000..c1ac789
--- /dev/null
+++ b/watch.cpp
@@ -0,0 +1,112 @@
+#include "watch.hpp"
+
+#include <sys/inotify.h>
+#include <unistd.h>
+
+#include <cstring>
+#include <filesystem>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+namespace phosphor
+{
+namespace certs
+{
+using namespace phosphor::logging;
+namespace fs = std::filesystem;
+using InternalFailure =
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+Watch::Watch(sdeventplus::Event& event, std::string& certFile, Callback cb) :
+ event(event), callback(cb)
+{
+ // get parent directory of certificate file to watch
+ fs::path path = std::move(fs::path(certFile).parent_path());
+ try
+ {
+ if (!fs::exists(path))
+ {
+ fs::create_directories(path);
+ }
+ }
+ catch (fs::filesystem_error& e)
+ {
+ log<level::ERR>("Failed to create directory", entry("ERR=%s", e.what()),
+ entry("DIRECTORY=%s", path.c_str()));
+ elog<InternalFailure>();
+ }
+ watchDir = path;
+ watchFile = fs::path(certFile).filename();
+ startWatch();
+}
+
+Watch::~Watch()
+{
+ stopWatch();
+}
+
+void Watch::startWatch()
+{
+ // stop any existing watch
+ stopWatch();
+
+ fd = inotify_init1(IN_NONBLOCK);
+ if (-1 == fd)
+ {
+ log<level::ERR>("inotify_init1 failed,",
+ entry("ERR=%s", std::strerror(errno)));
+ elog<InternalFailure>();
+ }
+ wd = inotify_add_watch(fd, watchDir.c_str(), IN_CLOSE_WRITE);
+ if (-1 == wd)
+ {
+ close(fd);
+ log<level::ERR>("inotify_add_watch failed,",
+ entry("ERR=%s", std::strerror(errno)),
+ entry("WATCH=%s", watchDir.c_str()));
+ elog<InternalFailure>();
+ }
+
+ ioPtr = std::make_unique<sdeventplus::source::IO>(
+ event, fd, EPOLLIN,
+ [this](sdeventplus::source::IO&, int fd, uint32_t revents) {
+ const int size = sizeof(struct inotify_event) + NAME_MAX + 1;
+ std::array<char, size> buffer;
+ int length = read(fd, buffer.data(), buffer.size());
+ if (length >= static_cast<int>(sizeof(struct inotify_event)))
+ {
+ struct inotify_event* notifyEvent =
+ reinterpret_cast<struct inotify_event*>(&buffer[0]);
+ if (notifyEvent->len)
+ {
+ if (watchFile == notifyEvent->name)
+ {
+ callback();
+ }
+ }
+ }
+ else
+ {
+ log<level::ERR>("Failed to read inotify event");
+ }
+ });
+}
+
+void Watch::stopWatch()
+{
+ if (-1 != fd)
+ {
+ if (-1 != wd)
+ {
+ inotify_rm_watch(fd, wd);
+ }
+ close(fd);
+ }
+ if (ioPtr)
+ {
+ ioPtr.reset();
+ }
+}
+
+} // namespace certs
+} // namespace phosphor