phosphor-ldap-conf: update nslcd.conf file with tls_cacertfile info

tls_cacertfile specifies the path to the X.509 certificate for
peer authentication.

Also updated the file with "tls_reqcert hard", to force the
behavior: if no certificate is provided, or a bad certificate
is provided, the session is immediately terminated.

Tested: tested using below given commands
1.curl -c cjar -b cjar -k -H "Content-Type: application/json" -X  POST -d \
  '{"data":[true,"ldaps://<host_ip>/","cn=<user-id>,dc=Corp,dc=ibm,dc=com",\
  "cn=Users,dc=Corp,dc=ibm,dc=com",  "<password>",\
  "xyz.openbmc_project.User.Ldap.Create.SearchScope.sub",\
  "xyz.openbmc_project.User.Ldap.Create.Type.ActiveDirectory"] \
}' https://$BMC_IP//xyz/openbmc_project/user/ldap/action/CreateConfig

2.curl -b cjar -k -H "Content-Type: application/json" -X PUT -d '{"data":true}'\
  https://$BMC_IP/xyz/openbmc_project/user/ldap/config/attr/SecureLDAP

3.curl -b cjar -k -H "Content-Type: application/json" -X PUT -d \
  '{"data":"ldap://<host_ip>/"}' \
  https://$BMC_IP/xyz/openbmc_project/ldap/config/attr/LDAPServerURI

when "/etc/ssl/certs/Root-CA.pem" doesn't exist on target, we get below
given exception(if we try to set SecureLDAP is true):
"DBusException: xyz.openbmc_project.Common.Error.NoCACertificate: \
Server's CA certificate has not been provided."

Change-Id: I56ffe8b08bb71307b4f2bfe9cf935b6113e4579a
Signed-off-by: Nagaraju Goruganti <ngorugan@in.ibm.com>
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Signed-off-by: Nagaraju Goruganti <ngorugan@in.ibm.com>
diff --git a/phosphor-ldap-config/ldap_configuration.cpp b/phosphor-ldap-config/ldap_configuration.cpp
index 31e4e75..be6c1cc 100644
--- a/phosphor-ldap-config/ldap_configuration.cpp
+++ b/phosphor-ldap-config/ldap_configuration.cpp
@@ -24,13 +24,13 @@
 using ConfigInfo = std::map<Key, Val>;
 
 Config::Config(sdbusplus::bus::bus& bus, const char* path, const char* filePath,
-               bool secureLDAP, std::string lDAPServerURI,
-               std::string lDAPBindDN, std::string lDAPBaseDN,
-               std::string&& lDAPBindDNPassword,
+               const char* caCertFile, bool secureLDAP,
+               std::string lDAPServerURI, std::string lDAPBindDN,
+               std::string lDAPBaseDN, std::string&& lDAPBindDNPassword,
                ldap_base::Config::SearchScope lDAPSearchScope,
                ldap_base::Config::Type lDAPType, ConfigMgr& parent) :
     ConfigIface(bus, path, true),
-    secureLDAP(secureLDAP), configFilePath(filePath),
+    secureLDAP(secureLDAP), configFilePath(filePath), tlsCacertFile(caCertFile),
     lDAPBindDNPassword(std::move(lDAPBindDNPassword)), bus(bus), parent(parent)
 {
     ConfigIface::lDAPServerURI(lDAPServerURI);
@@ -106,8 +106,8 @@
     if (secureLDAP == true)
     {
         confData << "ssl on\n";
-        confData << "tls_reqcert allow\n";
-        confData << "tls_cert /etc/nslcd/certs/cert.pem\n";
+        confData << "tls_reqcert hard\n";
+        confData << "tls_cacertFile " << tlsCacertFile.c_str() << "\n";
     }
     else
     {
@@ -192,6 +192,13 @@
             elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPServerURI"),
                                   Argument::ARGUMENT_VALUE(value.c_str()));
         }
+
+        if (secureLDAP && !fs::exists(tlsCacertFile.c_str()))
+        {
+            log<level::ERR>("LDAP server's CA certificate not provided",
+                            entry("TLSCACERTFILE=%s", tlsCacertFile.c_str()));
+            elog<NoCACertificate>();
+        }
         val = ConfigIface::lDAPServerURI(value);
         writeConfig();
         parent.restartService(nslcdService);
@@ -204,6 +211,10 @@
     {
         throw;
     }
+    catch (const NoCACertificate& e)
+    {
+        throw;
+    }
     catch (const std::exception& e)
     {
         log<level::ERR>(e.what());
@@ -405,6 +416,13 @@
                               Argument::ARGUMENT_VALUE(lDAPServerURI.c_str()));
     }
 
+    if (secureLDAP && !fs::exists(tlsCacertFile.c_str()))
+    {
+        log<level::ERR>("LDAP server's CA certificate not provided",
+                        entry("TLSCACERTFILE=%s", tlsCacertFile.c_str()));
+        elog<NoCACertificate>();
+    }
+
     if (lDAPBindDN.empty())
     {
         log<level::ERR>("Not a valid LDAP BINDDN",
@@ -438,8 +456,9 @@
 
     auto objPath = std::string(LDAP_CONFIG_DBUS_OBJ_PATH);
     configPtr = std::make_unique<Config>(
-        bus, objPath.c_str(), configFilePath.c_str(), secureLDAP, lDAPServerURI,
-        lDAPBindDN, lDAPBaseDN, std::move(lDAPBindDNPassword),
+        bus, objPath.c_str(), configFilePath.c_str(), tlsCacertFile.c_str(),
+        secureLDAP, lDAPServerURI, lDAPBindDN, lDAPBaseDN,
+        std::move(lDAPBindDNPassword),
         static_cast<ldap_base::Config::SearchScope>(lDAPSearchScope),
         static_cast<ldap_base::Config::Type>(lDAPType), *this);
 
@@ -571,6 +590,12 @@
         // object upon finding empty values in config, as
         // this can be a default config.
     }
+    catch (const NoCACertificate& e)
+    {
+        // Don't throw - we don't want to create a D-Bus
+        // object upon finding "ssl on" without having tls_cacertFile in place,
+        // as this can be a default config.
+    }
     catch (const InternalFailure& e)
     {
         throw;
diff --git a/phosphor-ldap-config/ldap_configuration.hpp b/phosphor-ldap-config/ldap_configuration.hpp
index 0dfb56d..0254d42 100644
--- a/phosphor-ldap-config/ldap_configuration.hpp
+++ b/phosphor-ldap-config/ldap_configuration.hpp
@@ -49,6 +49,7 @@
      *  @param[in] bus - Bus to attach to.
      *  @param[in] path - The D-Bus object path to attach at.
      *  @param[in] filePath - LDAP configuration file.
+     *  @param[in] caCertFile - LDAP's CA certificate file.
      *  @param[in] secureLDAP - Specifies whether to use SSL or not.
      *  @param[in] lDAPServerURI - LDAP URI of the server.
      *  @param[in] lDAPBindDN - distinguished name with which to bind.
@@ -61,8 +62,9 @@
      */
 
     Config(sdbusplus::bus::bus& bus, const char* path, const char* filePath,
-           bool secureLDAP, std::string lDAPServerURI, std::string lDAPBindDN,
-           std::string lDAPBaseDN, std::string&& lDAPBindDNPassword,
+           const char* caCertFile, bool secureLDAP, std::string lDAPServerURI,
+           std::string lDAPBindDN, std::string lDAPBaseDN,
+           std::string&& lDAPBindDNPassword,
            ldap_base::Config::SearchScope lDAPSearchScope,
            ldap_base::Config::Type lDAPType, ConfigMgr& parent);
 
@@ -112,6 +114,7 @@
 
   private:
     std::string configFilePath{};
+    std::string tlsCacertFile{};
     std::string lDAPBindDNPassword{};
 
     /** @brief Persistent sdbusplus D-Bus bus connection. */
@@ -144,10 +147,10 @@
      *  @param[in] bus - Bus to attach to.
      *  @param[in] path - Path to attach at.
      *  @param[in] filePath - LDAP configuration file.
-     *  @param[in] caCertfile - LDAP's CA certificate file.
+     *  @param[in] caCertFile - LDAP's CA certificate file.
      */
-    ConfigMgr(sdbusplus::bus::bus& bus, const char* path,
-              const char* filePath) :
+    ConfigMgr(sdbusplus::bus::bus& bus, const char* path, const char* filePath,
+              const char* caCertFile) :
         CreateIface(bus, path, true),
         configFilePath(filePath), bus(bus)
     {
@@ -198,7 +201,7 @@
 
   protected:
     std::string configFilePath{};
-    std::string tlsCacertfile{};
+    std::string tlsCacertFile{};
 
     /** @brief Persistent sdbusplus D-Bus bus connection. */
     sdbusplus::bus::bus& bus;
diff --git a/phosphor-ldap-config/main.cpp b/phosphor-ldap-config/main.cpp
index e285786..adbfdd3 100644
--- a/phosphor-ldap-config/main.cpp
+++ b/phosphor-ldap-config/main.cpp
@@ -27,7 +27,8 @@
     // Add sdbusplus ObjectManager for the 'root' path of the LDAP config.
     sdbusplus::server::manager::manager objManager(bus, LDAP_CONFIG_ROOT);
 
-    phosphor::ldap::ConfigMgr mgr(bus, LDAP_CONFIG_ROOT, LDAP_CONFIG_FILE);
+    phosphor::ldap::ConfigMgr mgr(bus, LDAP_CONFIG_ROOT, LDAP_CONFIG_FILE,
+                                  TLS_CACERT_FILE);
 
     bus.request_name(LDAP_CONFIG_BUSNAME);
 
diff --git a/test/ldap_config_test.cpp b/test/ldap_config_test.cpp
index 8f9a07c..a0447c9 100644
--- a/test/ldap_config_test.cpp
+++ b/test/ldap_config_test.cpp
@@ -65,8 +65,8 @@
 {
   public:
     MockConfigMgr(sdbusplus::bus::bus& bus, const char* path,
-                  const char* filePath) :
-        phosphor::ldap::ConfigMgr(bus, path, filePath)
+                  const char* filePath, const char* caCertFile) :
+        phosphor::ldap::ConfigMgr(bus, path, filePath, caCertFile)
     {
     }
     MOCK_METHOD1(restartService, void(const std::string& service));
@@ -88,13 +88,15 @@
 TEST_F(TestLDAPConfig, testCreate)
 {
     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
+    auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
 
     if (fs::exists(configFilePath))
     {
         fs::remove(configFilePath);
     }
     EXPECT_FALSE(fs::exists(configFilePath));
-    MockConfigMgr manager(bus, LDAP_CONFIG_ROOT, configFilePath.c_str());
+    MockConfigMgr manager(bus, LDAP_CONFIG_ROOT, configFilePath.c_str(),
+                          tlsCacertfile.c_str());
     EXPECT_CALL(manager, restartService("nslcd.service")).Times(2);
     EXPECT_CALL(manager, restartService("nscd.service")).Times(1);
     manager.createConfig("ldap://9.194.251.136/", "cn=Users,dc=com",
@@ -115,14 +117,15 @@
 TEST_F(TestLDAPConfig, testRestores)
 {
     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
+    auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
 
     if (fs::exists(configFilePath))
     {
         fs::remove(configFilePath);
     }
     EXPECT_FALSE(fs::exists(configFilePath));
-    MockConfigMgr* managerPtr =
-        new MockConfigMgr(bus, LDAP_CONFIG_ROOT, configFilePath.c_str());
+    MockConfigMgr* managerPtr = new MockConfigMgr(
+        bus, LDAP_CONFIG_ROOT, configFilePath.c_str(), tlsCacertfile.c_str());
     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(4);
     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(2);
     managerPtr->createConfig("ldap://9.194.251.138/", "cn=Users,dc=com",
@@ -150,14 +153,16 @@
 TEST_F(TestLDAPConfig, testLDAPServerURI)
 {
     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
+    auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
+
     if (fs::exists(configFilePath))
     {
         fs::remove(configFilePath);
     }
     EXPECT_FALSE(fs::exists(configFilePath));
-    MockConfigMgr* managerPtr =
-        new MockConfigMgr(bus, LDAP_CONFIG_ROOT, configFilePath.c_str());
-    EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(6);
+    MockConfigMgr* managerPtr = new MockConfigMgr(
+        bus, LDAP_CONFIG_ROOT, configFilePath.c_str(), tlsCacertfile.c_str());
+    EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(5);
     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(2);
 
     managerPtr->createConfig("ldap://9.194.251.138/", "cn=Users,dc=com",
@@ -165,33 +170,37 @@
                              ldap_base::Create::SearchScope::sub,
                              ldap_base::Create::Type::ActiveDirectory);
     // Change LDAP Server URI
-    managerPtr->getConfigPtr()->lDAPServerURI("ldap://9.194.251.139");
+    managerPtr->getConfigPtr()->lDAPServerURI("ldap://9.194.251.139/");
     EXPECT_EQ(managerPtr->getConfigPtr()->lDAPServerURI(),
-              "ldap://9.194.251.139");
+              "ldap://9.194.251.139/");
     // Change LDAP Server URI
-    managerPtr->getConfigPtr()->lDAPServerURI("ldaps://9.194.251.139");
+    EXPECT_THROW(
+        managerPtr->getConfigPtr()->lDAPServerURI("ldaps://9.194.251.139/"),
+        NoCACertificate);
     EXPECT_EQ(managerPtr->getConfigPtr()->lDAPServerURI(),
-              "ldaps://9.194.251.139");
+              "ldap://9.194.251.139/");
     // Delete LDAP configuration
     managerPtr->deleteObject();
 
     managerPtr->restore(configFilePath.c_str());
     // Check LDAP Server URI
     EXPECT_EQ(managerPtr->getConfigPtr()->lDAPServerURI(),
-              "ldaps://9.194.251.139");
+              "ldap://9.194.251.139/");
     delete managerPtr;
 }
 
 TEST_F(TestLDAPConfig, testLDAPBindDN)
 {
     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
+    auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
+
     if (fs::exists(configFilePath))
     {
         fs::remove(configFilePath);
     }
     EXPECT_FALSE(fs::exists(configFilePath));
-    MockConfigMgr* managerPtr =
-        new MockConfigMgr(bus, LDAP_CONFIG_ROOT, configFilePath.c_str());
+    MockConfigMgr* managerPtr = new MockConfigMgr(
+        bus, LDAP_CONFIG_ROOT, configFilePath.c_str(), tlsCacertfile.c_str());
     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(5);
     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(2);
 
@@ -230,13 +239,15 @@
 TEST_F(TestLDAPConfig, testLDAPBaseDN)
 {
     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
+    auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
+
     if (fs::exists(configFilePath))
     {
         fs::remove(configFilePath);
     }
     EXPECT_FALSE(fs::exists(configFilePath));
-    MockConfigMgr* managerPtr =
-        new MockConfigMgr(bus, LDAP_CONFIG_ROOT, configFilePath.c_str());
+    MockConfigMgr* managerPtr = new MockConfigMgr(
+        bus, LDAP_CONFIG_ROOT, configFilePath.c_str(), tlsCacertfile.c_str());
     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(5);
     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(2);
     managerPtr->createConfig("ldap://9.194.251.138/", "cn=Users,dc=com",
@@ -274,13 +285,15 @@
 TEST_F(TestLDAPConfig, testSearchScope)
 {
     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
+    auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
+
     if (fs::exists(configFilePath))
     {
         fs::remove(configFilePath);
     }
     EXPECT_FALSE(fs::exists(configFilePath));
-    MockConfigMgr* managerPtr =
-        new MockConfigMgr(bus, LDAP_CONFIG_ROOT, configFilePath.c_str());
+    MockConfigMgr* managerPtr = new MockConfigMgr(
+        bus, LDAP_CONFIG_ROOT, configFilePath.c_str(), tlsCacertfile.c_str());
     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(5);
     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(2);
     managerPtr->createConfig("ldap://9.194.251.138/", "cn=Users,dc=com",
@@ -305,13 +318,15 @@
 TEST_F(TestLDAPConfig, testLDAPType)
 {
     auto configFilePath = std::string(dir.c_str()) + "/" + ldapconfFile;
+    auto tlsCacertfile = std::string(dir.c_str()) + "/" + tslCacertFile;
+
     if (fs::exists(configFilePath))
     {
         fs::remove(configFilePath);
     }
     EXPECT_FALSE(fs::exists(configFilePath));
-    MockConfigMgr* managerPtr =
-        new MockConfigMgr(bus, LDAP_CONFIG_ROOT, configFilePath.c_str());
+    MockConfigMgr* managerPtr = new MockConfigMgr(
+        bus, LDAP_CONFIG_ROOT, configFilePath.c_str(), tlsCacertfile.c_str());
     EXPECT_CALL(*managerPtr, restartService("nslcd.service")).Times(5);
     EXPECT_CALL(*managerPtr, restartService("nscd.service")).Times(2);
     managerPtr->createConfig("ldap://9.194.251.138/", "cn=Users,dc=com",