1 #include "ldap_config_mgr.hpp"
2 
3 #include "ldap_config.hpp"
4 #include "utils.hpp"
5 
6 #include <phosphor-logging/elog-errors.hpp>
7 #include <phosphor-logging/elog.hpp>
8 #include <phosphor-logging/lg2.hpp>
9 #include <xyz/openbmc_project/Common/error.hpp>
10 
11 #include <filesystem>
12 #include <fstream>
13 #include <sstream>
14 
15 namespace phosphor
16 {
17 namespace ldap
18 {
19 
20 constexpr auto nslcdService = "nslcd.service";
21 constexpr auto nscdService = "nscd.service";
22 constexpr auto ldapScheme = "ldap";
23 constexpr auto ldapsScheme = "ldaps";
24 
25 using namespace phosphor::logging;
26 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
27 namespace fs = std::filesystem;
28 using Argument = xyz::openbmc_project::Common::InvalidArgument;
29 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed;
30 
31 using Line = std::string;
32 using Key = std::string;
33 using Val = std::string;
34 using ConfigInfo = std::map<Key, Val>;
35 
36 void ConfigMgr::startOrStopService(const std::string& service, bool start)
37 {
38     if (start)
39     {
40         restartService(service);
41     }
42     else
43     {
44         stopService(service);
45     }
46 }
47 
48 void ConfigMgr::restartService(const std::string& service)
49 {
50     try
51     {
52         auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
53                                           SYSTEMD_INTERFACE, "RestartUnit");
54         method.append(service.c_str(), "replace");
55         bus.call_noreply(method);
56     }
57     catch (const sdbusplus::exception_t& ex)
58     {
59         lg2::error("Failed to restart service {SERVICE}: {ERR}", "SERVICE",
60                    service, "ERR", ex);
61         elog<InternalFailure>();
62     }
63 }
64 void ConfigMgr::stopService(const std::string& service)
65 {
66     try
67     {
68         auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
69                                           SYSTEMD_INTERFACE, "StopUnit");
70         method.append(service.c_str(), "replace");
71         bus.call_noreply(method);
72     }
73     catch (const sdbusplus::exception_t& ex)
74     {
75         lg2::error("Failed to stop service {SERVICE}: {ERR}", "SERVICE",
76                    service, "ERR", ex);
77         elog<InternalFailure>();
78     }
79 }
80 
81 std::string ConfigMgr::createConfig(
82     std::string ldapServerURI, std::string ldapBindDN, std::string ldapBaseDN,
83     std::string ldapBindDNPassword, CreateIface::SearchScope ldapSearchScope,
84     CreateIface::Create::Type ldapType, std::string groupNameAttribute,
85     std::string userNameAttribute)
86 {
87     bool secureLDAP = false;
88 
89     if (isValidLDAPURI(ldapServerURI, ldapsScheme))
90     {
91         secureLDAP = true;
92     }
93     else if (isValidLDAPURI(ldapServerURI, ldapScheme))
94     {
95         secureLDAP = false;
96     }
97     else
98     {
99         lg2::error("Bad LDAP Server URI {URI}", "URI", ldapServerURI);
100         elog<InvalidArgument>(Argument::ARGUMENT_NAME("ldapServerURI"),
101                               Argument::ARGUMENT_VALUE(ldapServerURI.c_str()));
102     }
103 
104     if (secureLDAP && !fs::exists(tlsCacertFile.c_str()))
105     {
106         lg2::error("LDAP server CA certificate not found at {PATH}", "PATH",
107                    tlsCacertFile);
108         elog<NoCACertificate>();
109     }
110 
111     if (ldapBindDN.empty())
112     {
113         lg2::error("'{BINDDN}' is not a valid LDAP BindDN", "BINDDN",
114                    ldapBindDN);
115         elog<InvalidArgument>(Argument::ARGUMENT_NAME("LDAPBindDN"),
116                               Argument::ARGUMENT_VALUE(ldapBindDN.c_str()));
117     }
118 
119     if (ldapBaseDN.empty())
120     {
121         lg2::error("'{BASEDN}' is not a valid LDAP BaseDN", "BASEDN",
122                    ldapBaseDN);
123         elog<InvalidArgument>(Argument::ARGUMENT_NAME("LDAPBaseDN"),
124                               Argument::ARGUMENT_VALUE(ldapBaseDN.c_str()));
125     }
126 
127     // With current implementation we support only two default LDAP server.
128     // which will be always there but when the support comes for additional
129     // account providers then the create config would be used to create the
130     // additional config.
131 
132     std::string objPath;
133 
134     if (static_cast<ConfigIface::Type>(ldapType) == ConfigIface::Type::OpenLdap)
135     {
136         openLDAPConfigPtr.reset(nullptr);
137         objPath = openLDAPDbusObjectPath;
138         openLDAPConfigPtr = std::make_unique<Config>(
139             bus, objPath.c_str(), configFilePath.c_str(), tlsCacertFile.c_str(),
140             tlsCertFile.c_str(), secureLDAP, ldapServerURI, ldapBindDN,
141             ldapBaseDN, std::move(ldapBindDNPassword),
142             static_cast<ConfigIface::SearchScope>(ldapSearchScope),
143             static_cast<ConfigIface::Type>(ldapType), false, groupNameAttribute,
144             userNameAttribute, *this);
145     }
146     else
147     {
148         ADConfigPtr.reset(nullptr);
149         objPath = adDbusObjectPath;
150         ADConfigPtr = std::make_unique<Config>(
151             bus, objPath.c_str(), configFilePath.c_str(), tlsCacertFile.c_str(),
152             tlsCertFile.c_str(), secureLDAP, ldapServerURI, ldapBindDN,
153             ldapBaseDN, std::move(ldapBindDNPassword),
154             static_cast<ConfigIface::SearchScope>(ldapSearchScope),
155             static_cast<ConfigIface::Type>(ldapType), false, groupNameAttribute,
156             userNameAttribute, *this);
157     }
158     restartService(nscdService);
159     return objPath;
160 }
161 
162 void ConfigMgr::createDefaultObjects()
163 {
164     if (!openLDAPConfigPtr)
165     {
166         openLDAPConfigPtr = std::make_unique<Config>(
167             bus, openLDAPDbusObjectPath.c_str(), configFilePath.c_str(),
168             tlsCacertFile.c_str(), tlsCertFile.c_str(),
169             ConfigIface::Type::OpenLdap, *this);
170         openLDAPConfigPtr->emit_object_added();
171     }
172     if (!ADConfigPtr)
173     {
174         ADConfigPtr = std::make_unique<Config>(
175             bus, adDbusObjectPath.c_str(), configFilePath.c_str(),
176             tlsCacertFile.c_str(), tlsCertFile.c_str(),
177             ConfigIface::Type::ActiveDirectory, *this);
178         ADConfigPtr->emit_object_added();
179     }
180 }
181 
182 bool ConfigMgr::enableService(Config& config, bool value)
183 {
184     if (value)
185     {
186         if (openLDAPConfigPtr && openLDAPConfigPtr->enabled())
187         {
188             elog<NotAllowed>(NotAllowedArgument::REASON(
189                 "OpenLDAP service is already active"));
190         }
191         if (ADConfigPtr && ADConfigPtr->enabled())
192         {
193             elog<NotAllowed>(NotAllowedArgument::REASON(
194                 "ActiveDirectory service is already active"));
195         }
196     }
197     return config.enableService(value);
198 }
199 
200 void ConfigMgr::restore()
201 {
202     createDefaultObjects();
203     // Restore the ldap config and their mappings
204     if (ADConfigPtr->deserialize())
205     {
206         // Restore the role mappings
207         ADConfigPtr->restoreRoleMapping();
208         ADConfigPtr->emit_object_added();
209     }
210     if (openLDAPConfigPtr->deserialize())
211     {
212         // Restore the role mappings
213         openLDAPConfigPtr->restoreRoleMapping();
214         openLDAPConfigPtr->emit_object_added();
215     }
216 
217     startOrStopService(phosphor::ldap::nslcdService,
218                        ADConfigPtr->enabled() || openLDAPConfigPtr->enabled());
219 }
220 
221 } // namespace ldap
222 } // namespace phosphor
223