Implementation of certificate install interface
- Copy the certificate and private Key file to the service
specific path based on a configuration file.
- Reload the listed service for which the certificate is
updated.
Change-Id: Iae7d340a0a2381502aef33762eb79b57ddeda07d
Signed-off-by: Jayanth Othayoth <ojayanth@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 824d780..a597c38 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,6 +2,7 @@
# Build these headers, don't install them
noinst_HEADERS = \
+ certs_manager.hpp \
argument.hpp
sbin_PROGRAMS = \
@@ -9,4 +10,14 @@
phosphor_certificate_manager_SOURCES = \
mainapp.cpp \
+ certs_manager.cpp \
argument.cpp
+
+phosphor_certificate_manager_LDFLAGS = \
+ $(SDBUSPLUS_LIBS) \
+ $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+ -lstdc++fs
+
+phosphor_certificate_manager_CXXFLAGS = \
+ $(SYSTEMD_CFLAGS) \
+ $(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
diff --git a/certs_manager.cpp b/certs_manager.cpp
new file mode 100644
index 0000000..d1ff25d
--- /dev/null
+++ b/certs_manager.cpp
@@ -0,0 +1,93 @@
+#include "certs_manager.hpp"
+
+#include <experimental/filesystem>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace certs
+{
+
+using namespace phosphor::logging;
+using InternalFailure =
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+void Manager::install(const std::string path)
+{
+ // TODO Validate the certificate file
+
+ // Copy the certificate file
+ copy(path, certPath);
+
+ // Invoke type specific install function.
+ auto iter = typeFuncMap.find(type);
+ if (iter == typeFuncMap.end())
+ {
+ log<level::ERR>("Unsupported Type", entry("TYPE=%s", type.c_str()));
+ elog<InternalFailure>();
+ }
+ iter->second();
+}
+
+void Manager::serverInstall()
+{
+ if (!unit.empty())
+ {
+ reload(unit);
+ }
+}
+
+void Manager::clientInstall()
+{
+ // Do nothing now
+}
+
+void Manager::reload(const std::string& unit)
+{
+ constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
+ constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
+ constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
+
+ try
+ {
+ auto method = bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
+ SYSTEMD_INTERFACE, "ReloadUnit");
+
+ method.append(unit, "replace");
+
+ bus.call_noreply(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Failed to reload service", entry("ERR=%s", e.what()),
+ entry("UNIT=%s", unit.c_str()));
+ elog<InternalFailure>();
+ }
+}
+
+void Manager::copy(const std::string& src, const std::string& dst)
+{
+ namespace fs = std::experimental::filesystem;
+
+ try
+ {
+ auto path = fs::path(dst).parent_path();
+ // create dst path folder by default
+ fs::create_directories(path);
+ fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
+ }
+ catch (fs::filesystem_error& e)
+ {
+ log<level::ERR>("Failed to copy certificate", entry("ERR=%s", e.what()),
+ entry("SRC=%s", src.c_str()),
+ entry("DST=%s", dst.c_str()));
+ elog<InternalFailure>();
+ }
+}
+
+} // namespace certs
+} // namespace phosphor
diff --git a/certs_manager.hpp b/certs_manager.hpp
new file mode 100644
index 0000000..a23f044
--- /dev/null
+++ b/certs_manager.hpp
@@ -0,0 +1,107 @@
+#pragma once
+#include <cstring>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <unordered_map>
+#include <xyz/openbmc_project/Certs/Install/server.hpp>
+
+namespace phosphor
+{
+namespace certs
+{
+
+// Supported Types.
+static constexpr auto SERVER = "server";
+static constexpr auto CLIENT = "client";
+
+using CreateIface = sdbusplus::server::object::object<
+ sdbusplus::xyz::openbmc_project::Certs::server::Install>;
+using InstallFunc = std::function<void()>;
+using InputType = std::string;
+
+class Manager : public CreateIface
+{
+ public:
+ /* Define all of the basic class operations:
+ * Not allowed:
+ * - Default constructor is not possible due to member
+ * reference
+ * - Move operations due to 'this' being registered as the
+ * 'context' with sdbus.
+ * Allowed:
+ * - copy
+ * - Destructor.
+ */
+ Manager() = delete;
+ Manager(const Manager&) = default;
+ Manager& operator=(const Manager&) = delete;
+ Manager(Manager&&) = delete;
+ Manager& operator=(Manager&&) = delete;
+ virtual ~Manager() = default;
+
+ /** @brief Constructor to put object onto bus at a dbus path.
+ * @param[in] bus - Bus to attach to.
+ * @param[in] path - Path to attach at.
+ * @param[in] type - Type of the certificate.
+ * @param[in] unit - Unit consumed by this certificate.
+ * @param[in] certpath - Certificate installation path.
+ */
+ Manager(sdbusplus::bus::bus& bus, const char* path, const std::string& type,
+ std::string&& unit, std::string&& certPath) :
+ CreateIface(bus, path),
+ bus(bus), path(path), type(type), unit(std::move(unit)),
+ certPath(std::move(certPath))
+ {
+ typeFuncMap[SERVER] =
+ std::bind(&phosphor::certs::Manager::serverInstall, this);
+ typeFuncMap[CLIENT] =
+ std::bind(&phosphor::certs::Manager::clientInstall, this);
+ }
+
+ /** @brief Implementation for Install
+ * Replace the existing certificate key file with another
+ * (possibly CA signed) Certificate key file.
+ *
+ * @param[in] path - Certificate key file path.
+ */
+ void install(const std::string path) override;
+
+ private:
+ /** @brief Client certificate Installation helper function **/
+ void clientInstall();
+
+ /** @brief Server certificate Installation helper function **/
+ void serverInstall();
+
+ /** @brief systemd unit reload helper function
+ * @param[in] unit - service need to reload.
+ */
+ void reload(const std::string& unit);
+
+ /** @brief helper function to copy the file.
+ * @param[in] src - Source file path to copy
+ * @param[in] dst - Destination path to copy
+ */
+ void copy(const std::string& src, const std::string& dst);
+
+ /** @brief sdbusplus handler */
+ sdbusplus::bus::bus& bus;
+
+ /** @brief object path */
+ std::string path;
+
+ /** @brief Type of the certificate **/
+ InputType type;
+
+ /** @brief Unit name associated to the service **/
+ std::string unit;
+
+ /** @brief Certificate file installation path **/
+ std::string certPath;
+
+ /** @brief Type specific function pointer map **/
+ std::unordered_map<InputType, InstallFunc> typeFuncMap;
+};
+
+} // namespace certs
+} // namespace phosphor
diff --git a/configure.ac b/configure.ac
index e89eea5..02a0a94 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,6 +18,24 @@
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
+# Check for libraries
+PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces],,\
+ AC_MSG_ERROR(["Requires phosphor-dbus-interfaces package."]))
+PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],,
+ AC_MSG_ERROR(["Requires sdbusplus package."]))
+PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],,\
+ AC_MSG_ERROR(["Requires phosphor-logging package."]))
+
+# Code coverage
+AX_CODE_COVERAGE
+
+AC_ARG_VAR(BUSNAME, [The D-Bus busname to own])
+AS_IF([test "x$BUSNAME" == "x"], [BUSNAME="xyz.openbmc_project.Certs.Manager"])
+AC_DEFINE_UNQUOTED([BUSNAME], ["$BUSNAME"], [The D-Bus busname to own])
+AC_ARG_VAR(OBJPATH, [The certificate manager D-Bus root])
+AS_IF([test "x$OBJPATH" == "x"], [OBJPATH="/xyz/openbmc_project/certs"])
+AC_DEFINE_UNQUOTED([OBJPATH], ["$OBJPATH"], [The certificate manager D-Bus root])
+
# Create configured output
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
diff --git a/mainapp.cpp b/mainapp.cpp
index bbe2a85..5b0f29f 100644
--- a/mainapp.cpp
+++ b/mainapp.cpp
@@ -14,9 +14,13 @@
* limitations under the License.
*/
+#include "config.h"
+
#include "argument.hpp"
+#include "certs_manager.hpp"
#include <iostream>
+#include <locale>
#include <string>
static void ExitWithError(const char* err, char** argv)
@@ -27,6 +31,11 @@
exit(EXIT_FAILURE);
}
+inline void capitalize(std::string& s)
+{
+ s[0] = std::toupper(s[0]);
+}
+
int main(int argc, char** argv)
{
// Read arguments.
@@ -34,9 +43,11 @@
// Parse arguments
auto type = std::move((options)["type"]);
- if (type == phosphor::certs::util::ArgumentParser::empty_string)
+ if ((type == phosphor::certs::util::ArgumentParser::empty_string) ||
+ !((type == phosphor::certs::SERVER) ||
+ (type == phosphor::certs::CLIENT)))
{
- ExitWithError("type not specified.", argv);
+ ExitWithError("type not specified or invalid.", argv);
}
auto endpoint = std::move((options)["endpoint"]);
@@ -51,8 +62,27 @@
ExitWithError("path not specified.", argv);
}
- // unit is an optional parametr
+ // unit is an optional parameter
auto unit = std::move((options)["unit"]);
+ auto bus = sdbusplus::bus::new_default();
+
+ auto objPath = std::string(OBJPATH) + '/' + type + '/' + endpoint;
+
+ phosphor::certs::Manager manager(bus, objPath.c_str(), type,
+ std::move(unit), std::move(path));
+
+ // Adjusting Interface name as per std convention
+ capitalize(type);
+ capitalize(endpoint);
+ auto busName = std::string(BUSNAME) + '.' + type + '.' + endpoint;
+ bus.request_name(busName.c_str());
+
+ while (true)
+ {
+ // process dbus calls / signals discarding unhandled
+ bus.process_discard();
+ bus.wait();
+ }
return 0;
}