1 #include "ldap_config_mgr.hpp"
2 #include "ldap_config.hpp"
3 #include "ldap_config_serialize.hpp"
4 #include "utils.hpp"
5 #include <filesystem>
6 #include <fstream>
7 #include <sstream>
8 
9 namespace phosphor
10 {
11 namespace ldap
12 {
13 
14 constexpr auto nslcdService = "nslcd.service";
15 constexpr auto nscdService = "nscd.service";
16 constexpr auto LDAPscheme = "ldap";
17 constexpr auto LDAPSscheme = "ldaps";
18 
19 using namespace phosphor::logging;
20 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
21 namespace fs = std::filesystem;
22 using Argument = xyz::openbmc_project::Common::InvalidArgument;
23 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
24 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed;
25 
26 using Line = std::string;
27 using Key = std::string;
28 using Val = std::string;
29 using ConfigInfo = std::map<Key, Val>;
30 
31 Config::Config(sdbusplus::bus::bus& bus, const char* path, const char* filePath,
32                const char* caCertFile, bool secureLDAP,
33                std::string lDAPServerURI, std::string lDAPBindDN,
34                std::string lDAPBaseDN, std::string&& lDAPBindDNPassword,
35                ConfigIface::SearchScope lDAPSearchScope,
36                ConfigIface::Type lDAPType, bool lDAPServiceEnabled,
37                std::string userNameAttr, std::string groupNameAttr,
38                ConfigMgr& parent) :
39     Ifaces(bus, path, true),
40     secureLDAP(secureLDAP), lDAPBindPassword(std::move(lDAPBindDNPassword)),
41     configFilePath(filePath), tlsCacertFile(caCertFile), bus(bus),
42     parent(parent)
43 {
44     ConfigIface::lDAPServerURI(lDAPServerURI);
45     ConfigIface::lDAPBindDN(lDAPBindDN);
46     ConfigIface::lDAPBaseDN(lDAPBaseDN);
47     ConfigIface::lDAPSearchScope(lDAPSearchScope);
48     ConfigIface::lDAPType(lDAPType);
49     EnableIface::enabled(lDAPServiceEnabled);
50     ConfigIface::userNameAttribute(userNameAttr);
51     ConfigIface::groupNameAttribute(groupNameAttr);
52     // Don't update the bindDN password under ConfigIface::
53     writeConfig();
54     // Emit deferred signal.
55     this->emit_object_added();
56     parent.startOrStopService(nslcdService, enabled());
57 }
58 
59 void Config::delete_()
60 {
61     parent.deleteObject();
62     try
63     {
64         fs::path configDir = fs::path(configFilePath.c_str()).parent_path();
65 
66         fs::copy_file(configDir / defaultNslcdFile, LDAP_CONFIG_FILE,
67                       fs::copy_options::overwrite_existing);
68     }
69     catch (const std::exception& e)
70     {
71         log<level::ERR>("Failed to rename Config Files while deleting Object",
72                         entry("ERR=%s", e.what()));
73         elog<InternalFailure>();
74     }
75 
76     parent.restartService(nscdService);
77     parent.stopService(nslcdService);
78 }
79 
80 void Config::writeConfig()
81 {
82     std::stringstream confData;
83     auto isPwdTobeWritten = false;
84     std::string userNameAttr;
85 
86     confData << "uid root\n";
87     confData << "gid root\n\n";
88     confData << "ldap_version 3\n\n";
89     confData << "timelimit 30\n";
90     confData << "bind_timelimit 30\n";
91     confData << "pagesize 1000\n";
92     confData << "referrals off\n\n";
93     confData << "uri " << lDAPServerURI() << "\n\n";
94     confData << "base " << lDAPBaseDN() << "\n\n";
95     confData << "binddn " << lDAPBindDN() << "\n";
96     if (!lDAPBindPassword.empty())
97     {
98         confData << "bindpw " << lDAPBindPassword << "\n";
99         isPwdTobeWritten = true;
100     }
101     confData << "\n";
102     switch (lDAPSearchScope())
103     {
104         case ConfigIface::SearchScope::sub:
105             confData << "scope sub\n\n";
106             break;
107         case ConfigIface::SearchScope::one:
108             confData << "scope one\n\n";
109             break;
110         case ConfigIface::SearchScope::base:
111             confData << "scope base\n\n";
112             break;
113     }
114     confData << "base passwd " << lDAPBaseDN() << "\n";
115     confData << "base shadow " << lDAPBaseDN() << "\n\n";
116     if (secureLDAP == true)
117     {
118         confData << "ssl on\n";
119         confData << "tls_reqcert hard\n";
120         confData << "tls_cacertFile " << tlsCacertFile.c_str() << "\n";
121     }
122     else
123     {
124         confData << "ssl off\n";
125     }
126     confData << "\n";
127     if (lDAPType() == ConfigIface::Type::ActiveDirectory)
128     {
129         if (ConfigIface::userNameAttribute().empty())
130         {
131             ConfigIface::userNameAttribute("sAMAccountName");
132         }
133         if (ConfigIface::groupNameAttribute().empty())
134         {
135             ConfigIface::groupNameAttribute("primaryGroupID");
136         }
137         confData << "filter passwd (&(objectClass=user)(objectClass=person)"
138                     "(!(objectClass=computer)))\n";
139         confData
140             << "filter group (|(objectclass=group)(objectclass=groupofnames) "
141                "(objectclass=groupofuniquenames))\n";
142         confData << "map passwd uid              "
143                  << ConfigIface::userNameAttribute() << "\n";
144         confData << "map passwd uidNumber        "
145                     "objectSid:S-1-5-21-3623811015-3361044348-30300820\n";
146         confData << "map passwd gidNumber        "
147                  << ConfigIface::groupNameAttribute() << "\n";
148         confData << "map passwd homeDirectory    \"/home/$sAMAccountName\"\n";
149         confData << "map passwd gecos            displayName\n";
150         confData << "map passwd loginShell       \"/bin/bash\"\n";
151         confData << "map group gidNumber         "
152                     "objectSid:S-1-5-21-3623811015-3361044348-30300820\n";
153         confData << "map group cn                "
154                  << ConfigIface::userNameAttribute() << "\n";
155     }
156     else if (lDAPType() == ConfigIface::Type::OpenLdap)
157     {
158         if (ConfigIface::userNameAttribute().empty())
159         {
160             ConfigIface::userNameAttribute("cn");
161         }
162         if (ConfigIface::groupNameAttribute().empty())
163         {
164             ConfigIface::groupNameAttribute("gidNumber");
165         }
166         confData << "filter passwd (objectclass=*)\n";
167         confData << "map passwd gecos displayName\n";
168         confData << "filter group (objectclass=posixGroup)\n";
169         confData << "map passwd uid              "
170                  << ConfigIface::userNameAttribute() << "\n";
171         confData << "map passwd gidNumber        "
172                  << ConfigIface::groupNameAttribute() << "\n";
173     }
174     try
175     {
176         std::fstream stream(configFilePath.c_str(), std::fstream::out);
177         // remove the read permission from others if password is being written.
178         // nslcd forces this behaviour.
179         auto permission = fs::perms::owner_read | fs::perms::owner_write |
180                           fs::perms::group_read;
181         if (isPwdTobeWritten)
182         {
183             fs::permissions(configFilePath, permission);
184         }
185         else
186         {
187             fs::permissions(configFilePath,
188                             permission | fs::perms::others_read);
189         }
190 
191         stream << confData.str();
192         stream.flush();
193         stream.close();
194     }
195     catch (const std::exception& e)
196     {
197         log<level::ERR>(e.what());
198         elog<InternalFailure>();
199     }
200     return;
201 }
202 
203 std::string Config::lDAPBindDNPassword(std::string value)
204 {
205     // Don't update the D-bus object, this is just to
206     // facilitate if user wants to change the bind dn password
207     // once d-bus object gets created.
208     lDAPBindPassword = value;
209     try
210     {
211         writeConfig();
212         parent.startOrStopService(nslcdService, enabled());
213     }
214     catch (const InternalFailure& e)
215     {
216         throw;
217     }
218     catch (const std::exception& e)
219     {
220         log<level::ERR>(e.what());
221         elog<InternalFailure>();
222     }
223     return value;
224 }
225 
226 std::string Config::lDAPServerURI(std::string value)
227 {
228     std::string val;
229     try
230     {
231         if (value == lDAPServerURI())
232         {
233             return value;
234         }
235         if (isValidLDAPURI(value, LDAPSscheme))
236         {
237             secureLDAP = true;
238         }
239         else if (isValidLDAPURI(value, LDAPscheme))
240         {
241             secureLDAP = false;
242         }
243         else
244         {
245             log<level::ERR>("bad LDAP Server URI",
246                             entry("LDAPSERVERURI=%s", value.c_str()));
247             elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPServerURI"),
248                                   Argument::ARGUMENT_VALUE(value.c_str()));
249         }
250 
251         if (secureLDAP && !fs::exists(tlsCacertFile.c_str()))
252         {
253             log<level::ERR>("LDAP server's CA certificate not provided",
254                             entry("TLSCACERTFILE=%s", tlsCacertFile.c_str()));
255             elog<NoCACertificate>();
256         }
257         val = ConfigIface::lDAPServerURI(value);
258         writeConfig();
259         parent.startOrStopService(nslcdService, enabled());
260     }
261     catch (const InternalFailure& e)
262     {
263         throw;
264     }
265     catch (const InvalidArgument& e)
266     {
267         throw;
268     }
269     catch (const NoCACertificate& e)
270     {
271         throw;
272     }
273     catch (const std::exception& e)
274     {
275         log<level::ERR>(e.what());
276         elog<InternalFailure>();
277     }
278     return val;
279 }
280 
281 std::string Config::lDAPBindDN(std::string value)
282 {
283     std::string val;
284     try
285     {
286         if (value == lDAPBindDN())
287         {
288             return value;
289         }
290 
291         if (value.empty())
292         {
293             log<level::ERR>("Not a valid LDAP BINDDN",
294                             entry("LDAPBINDDN=%s", value.c_str()));
295             elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPBindDN"),
296                                   Argument::ARGUMENT_VALUE(value.c_str()));
297         }
298 
299         val = ConfigIface::lDAPBindDN(value);
300         writeConfig();
301         parent.startOrStopService(nslcdService, enabled());
302     }
303     catch (const InternalFailure& e)
304     {
305         throw;
306     }
307     catch (const InvalidArgument& e)
308     {
309         throw;
310     }
311     catch (const std::exception& e)
312     {
313         log<level::ERR>(e.what());
314         elog<InternalFailure>();
315     }
316     return val;
317 }
318 
319 std::string Config::lDAPBaseDN(std::string value)
320 {
321     std::string val;
322     try
323     {
324         if (value == lDAPBaseDN())
325         {
326             return value;
327         }
328 
329         if (value.empty())
330         {
331             log<level::ERR>("Not a valid LDAP BASEDN",
332                             entry("BASEDN=%s", value.c_str()));
333             elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPBaseDN"),
334                                   Argument::ARGUMENT_VALUE(value.c_str()));
335         }
336 
337         val = ConfigIface::lDAPBaseDN(value);
338         writeConfig();
339         parent.startOrStopService(nslcdService, enabled());
340     }
341     catch (const InternalFailure& e)
342     {
343         throw;
344     }
345     catch (const InvalidArgument& e)
346     {
347         throw;
348     }
349     catch (const std::exception& e)
350     {
351         log<level::ERR>(e.what());
352         elog<InternalFailure>();
353     }
354     return val;
355 }
356 
357 ConfigIface::SearchScope Config::lDAPSearchScope(ConfigIface::SearchScope value)
358 {
359     ConfigIface::SearchScope val;
360     try
361     {
362         if (value == lDAPSearchScope())
363         {
364             return value;
365         }
366 
367         val = ConfigIface::lDAPSearchScope(value);
368         writeConfig();
369         parent.startOrStopService(nslcdService, enabled());
370     }
371     catch (const InternalFailure& e)
372     {
373         throw;
374     }
375     catch (const std::exception& e)
376     {
377         log<level::ERR>(e.what());
378         elog<InternalFailure>();
379     }
380     return val;
381 }
382 
383 ConfigIface::Type Config::lDAPType(ConfigIface::Type value)
384 {
385     elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
386     return lDAPType();
387 }
388 
389 bool Config::enabled(bool value)
390 {
391     bool isEnable;
392     try
393     {
394         if (value == enabled())
395         {
396             return value;
397         }
398         isEnable = EnableIface::enabled(value);
399         // save the enabled property.
400         serialize(*this, parent.dbusPersistentPath);
401         parent.startOrStopService(nslcdService, value);
402     }
403     catch (const InternalFailure& e)
404     {
405         throw;
406     }
407     catch (const std::exception& e)
408     {
409         log<level::ERR>(e.what());
410         elog<InternalFailure>();
411     }
412     return isEnable;
413 }
414 
415 std::string Config::userNameAttribute(std::string value)
416 {
417     std::string val;
418     try
419     {
420         if (value == userNameAttribute())
421         {
422             return value;
423         }
424 
425         val = ConfigIface::userNameAttribute(value);
426         writeConfig();
427         parent.startOrStopService(nslcdService, enabled());
428     }
429     catch (const InternalFailure& e)
430     {
431         throw;
432     }
433     catch (const std::exception& e)
434     {
435         log<level::ERR>(e.what());
436         elog<InternalFailure>();
437     }
438     return val;
439 }
440 
441 std::string Config::groupNameAttribute(std::string value)
442 {
443     std::string val;
444     try
445     {
446         if (value == groupNameAttribute())
447         {
448             return value;
449         }
450 
451         val = ConfigIface::groupNameAttribute(value);
452         writeConfig();
453         parent.startOrStopService(nslcdService, enabled());
454     }
455     catch (const InternalFailure& e)
456     {
457         throw;
458     }
459     catch (const std::exception& e)
460     {
461         log<level::ERR>(e.what());
462         elog<InternalFailure>();
463     }
464     return val;
465 }
466 
467 } // namespace ldap
468 } // namespace phosphor
469