phosphor-ldap-conf: validate LDAP Server URI

Validates given URI.
Also updates secureLDAP property based on given URI. If URI is of LDAPS type,
secureLDAP is set to true, else it is set to false.

Change-Id: If96495c01a8bd911d255267ffbbbff7f28fa070b
Signed-off-by: Nagaraju Goruganti <ngorugan@in.ibm.com>
diff --git a/phosphor-ldap-config/Makefile.am b/phosphor-ldap-config/Makefile.am
index cf2b4f0..907c365 100644
--- a/phosphor-ldap-config/Makefile.am
+++ b/phosphor-ldap-config/Makefile.am
@@ -1,9 +1,10 @@
 sbin_PROGRAMS = phosphor-ldap-conf
 
-noinst_HEADERS = ldap_configuration.hpp
+noinst_HEADERS = ldap_configuration.hpp utils.hpp
 
 phosphor_ldap_conf_SOURCES = \
                 main.cpp \
+                utils.cpp \
                 ldap_configuration.cpp
 
 phosphor_ldap_conf_LDFLAGS = $(SDBUSPLUS_LIBS) \
diff --git a/phosphor-ldap-config/ldap_configuration.cpp b/phosphor-ldap-config/ldap_configuration.cpp
index e84e0b9..6fdc511 100644
--- a/phosphor-ldap-config/ldap_configuration.cpp
+++ b/phosphor-ldap-config/ldap_configuration.cpp
@@ -1,5 +1,5 @@
 #include "ldap_configuration.hpp"
-#include <ldap.h>
+#include "utils.hpp"
 #include <experimental/filesystem>
 #include <fstream>
 #include <sstream>
@@ -10,6 +10,8 @@
 {
 constexpr auto nslcdService = "nslcd.service";
 constexpr auto nscdService = "nscd.service";
+constexpr auto LDAPscheme = "ldap";
+constexpr auto LDAPSscheme = "ldaps";
 
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
@@ -174,25 +176,20 @@
         {
             return value;
         }
-        if (secureLDAP)
+        if (isValidLDAPURI(value, LDAPSscheme))
         {
-            if (!ldap_is_ldaps_url(value.c_str()))
-            {
-                log<level::ERR>("bad LDAPS Server URI",
-                                entry("LDAPSSERVERURI=%s", value.c_str()));
-                elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPServerURI"),
-                                      Argument::ARGUMENT_VALUE(value.c_str()));
-            }
+            secureLDAP = true;
+        }
+        else if (isValidLDAPURI(value, LDAPscheme))
+        {
+            secureLDAP = false;
         }
         else
         {
-            if (!ldap_is_ldap_url(value.c_str()))
-            {
-                log<level::ERR>("bad LDAP Server URI",
-                                entry("LDAPSERVERURI=%s", value.c_str()));
-                elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPServerURI"),
-                                      Argument::ARGUMENT_VALUE(value.c_str()));
-            }
+            log<level::ERR>("bad LDAP Server URI",
+                            entry("LDAPSERVERURI=%s", value.c_str()));
+            elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPServerURI"),
+                                  Argument::ARGUMENT_VALUE(value.c_str()));
         }
         val = ConfigIface::lDAPServerURI(value);
         writeConfig();
@@ -202,6 +199,10 @@
     {
         throw;
     }
+    catch (const InvalidArgument& e)
+    {
+        throw;
+    }
     catch (const std::exception& e)
     {
         log<level::ERR>(e.what());
@@ -222,8 +223,8 @@
 
         if (value.empty())
         {
-            log<level::ERR>("Not a valid LDAP BINDDN"),
-                entry("LDAPBINDDN=%s", value.c_str());
+            log<level::ERR>("Not a valid LDAP BINDDN",
+                            entry("LDAPBINDDN=%s", value.c_str()));
             elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPBindDN"),
                                   Argument::ARGUMENT_VALUE(value.c_str()));
         }
@@ -256,8 +257,8 @@
 
         if (value.empty())
         {
-            log<level::ERR>("Not a valid LDAP BASEDN"),
-                entry("BASEDN=%s", value.c_str());
+            log<level::ERR>("Not a valid LDAP BASEDN",
+                            entry("BASEDN=%s", value.c_str()));
             elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPBaseDN"),
                                   Argument::ARGUMENT_VALUE(value.c_str()));
         }
@@ -379,11 +380,11 @@
 {
     bool secureLDAP = false;
 
-    if (ldap_is_ldaps_url(lDAPServerURI.c_str()))
+    if (isValidLDAPURI(lDAPServerURI, LDAPSscheme))
     {
         secureLDAP = true;
     }
-    else if (ldap_is_ldap_url(lDAPServerURI.c_str()))
+    else if (isValidLDAPURI(lDAPServerURI, LDAPscheme))
     {
         secureLDAP = false;
     }
@@ -397,16 +398,16 @@
 
     if (lDAPBindDN.empty())
     {
-        log<level::ERR>("Not a valid LDAP BINDDN"),
-            entry("LDAPBINDDN=%s", lDAPBindDN.c_str());
+        log<level::ERR>("Not a valid LDAP BINDDN",
+                        entry("LDAPBINDDN=%s", lDAPBindDN.c_str()));
         elog<InvalidArgument>(Argument::ARGUMENT_NAME("LDAPBindDN"),
                               Argument::ARGUMENT_VALUE(lDAPBindDN.c_str()));
     }
 
     if (lDAPBaseDN.empty())
     {
-        log<level::ERR>("Not a valid LDAP BASEDN"),
-            entry("LDAPBASEDN=%s", lDAPBaseDN.c_str());
+        log<level::ERR>("Not a valid LDAP BASEDN",
+                        entry("LDAPBASEDN=%s", lDAPBaseDN.c_str()));
         elog<InvalidArgument>(Argument::ARGUMENT_NAME("LDAPBaseDN"),
                               Argument::ARGUMENT_VALUE(lDAPBaseDN.c_str()));
     }
diff --git a/phosphor-ldap-config/utils.cpp b/phosphor-ldap-config/utils.cpp
new file mode 100644
index 0000000..7a40a3e
--- /dev/null
+++ b/phosphor-ldap-config/utils.cpp
@@ -0,0 +1,52 @@
+#include "utils.hpp"
+#include <cstring>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <ldap.h>
+#include <memory>
+
+namespace phosphor
+{
+namespace ldap
+{
+
+bool isValidLDAPURI(const std::string& URI, const char* scheme)
+{
+    LDAPURLDesc* ludpp = nullptr;
+    int res = LDAP_URL_ERR_BADURL;
+    res = ldap_url_parse(URI.c_str(), &ludpp);
+
+    auto ludppCleanupFunc = [](LDAPURLDesc* ludpp) {
+        ldap_free_urldesc(ludpp);
+    };
+    std::unique_ptr<LDAPURLDesc, decltype(ludppCleanupFunc)> ludppPtr(
+        ludpp, ludppCleanupFunc);
+
+    if (res != LDAP_URL_SUCCESS)
+    {
+        return false;
+    }
+    if (std::strcmp(scheme, ludppPtr->lud_scheme) != 0)
+    {
+        return false;
+    }
+    addrinfo hints{};
+    addrinfo* servinfo = nullptr;
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags |= AI_CANONNAME;
+
+    auto result = getaddrinfo(ludppPtr->lud_host, nullptr, &hints, &servinfo);
+    auto cleanupFunc = [](addrinfo* servinfo) { freeaddrinfo(servinfo); };
+    std::unique_ptr<addrinfo, decltype(cleanupFunc)> servinfoPtr(servinfo,
+                                                                 cleanupFunc);
+
+    if (result)
+    {
+        return false;
+    }
+    return true;
+}
+
+} // namespace ldap
+} // namespace phosphor
diff --git a/phosphor-ldap-config/utils.hpp b/phosphor-ldap-config/utils.hpp
new file mode 100644
index 0000000..984aae6
--- /dev/null
+++ b/phosphor-ldap-config/utils.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <string>
+
+namespace phosphor
+{
+namespace ldap
+{
+
+/** @brief checks that the given URI is valid LDAP's URI.
+ *      LDAP's URL begins with "ldap://" and LDAPS's URL begins with "ldap://"
+ *  @param[in] URI - URI which needs to be validated.
+ *  @param[in] scheme - LDAP's scheme, scheme equals to "ldaps" to validate
+ *       against LDAPS type URI, for LDAP type URI it is equals to "ldap".
+ *  @returns true if it is valid otherwise false.
+ */
+bool isValidLDAPURI(const std::string& URI, const char* scheme);
+
+} // namespace ldap
+} // namespace phosphor