1 #include "ldap_config_mgr.hpp"
2 #include "ldap_config.hpp"
3 #include "utils.hpp"
4 
5 #include <cereal/types/string.hpp>
6 #include <cereal/types/vector.hpp>
7 #include <cereal/archives/binary.hpp>
8 #include <filesystem>
9 #include <fstream>
10 #include <sstream>
11 
12 // Register class version
13 // From cereal documentation;
14 // "This macro should be placed at global scope"
15 CEREAL_CLASS_VERSION(phosphor::ldap::Config, CLASS_VERSION);
16 
17 namespace phosphor
18 {
19 namespace ldap
20 {
21 
22 constexpr auto nslcdService = "nslcd.service";
23 constexpr auto nscdService = "nscd.service";
24 constexpr auto LDAPscheme = "ldap";
25 constexpr auto LDAPSscheme = "ldaps";
26 
27 using namespace phosphor::logging;
28 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
29 namespace fs = std::filesystem;
30 using Argument = xyz::openbmc_project::Common::InvalidArgument;
31 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
32 using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed;
33 
34 using Line = std::string;
35 using Key = std::string;
36 using Val = std::string;
37 using ConfigInfo = std::map<Key, Val>;
38 
39 Config::Config(sdbusplus::bus::bus& bus, const char* path, const char* filePath,
40                const char* caCertFile, bool secureLDAP,
41                std::string lDAPServerURI, std::string lDAPBindDN,
42                std::string lDAPBaseDN, std::string&& lDAPBindDNPassword,
43                ConfigIface::SearchScope lDAPSearchScope,
44                ConfigIface::Type lDAPType, bool lDAPServiceEnabled,
45                std::string userNameAttr, std::string groupNameAttr,
46                ConfigMgr& parent) :
47     Ifaces(bus, path, true),
48     secureLDAP(secureLDAP), lDAPBindPassword(std::move(lDAPBindDNPassword)),
49     tlsCacertFile(caCertFile), configFilePath(filePath), objectPath(path),
50     bus(bus), parent(parent)
51 {
52     ConfigIface::lDAPServerURI(lDAPServerURI);
53     ConfigIface::lDAPBindDN(lDAPBindDN);
54     ConfigIface::lDAPBaseDN(lDAPBaseDN);
55     ConfigIface::lDAPSearchScope(lDAPSearchScope);
56     ConfigIface::lDAPType(lDAPType);
57     EnableIface::enabled(lDAPServiceEnabled);
58     ConfigIface::userNameAttribute(userNameAttr);
59     ConfigIface::groupNameAttribute(groupNameAttr);
60     // NOTE: Don't update the bindDN password under ConfigIface
61     if (enabled())
62     {
63         writeConfig();
64     }
65     // save the config.
66     configPersistPath = parent.dbusPersistentPath;
67     configPersistPath += objectPath;
68 
69     // create the persistent directory
70     fs::create_directories(configPersistPath);
71 
72     configPersistPath += "/config";
73 
74     std::ofstream os(configPersistPath, std::ios::binary | std::ios::out);
75     // remove the read permission from others
76     auto permission =
77         fs::perms::owner_read | fs::perms::owner_write | fs::perms::group_read;
78     fs::permissions(configPersistPath, permission);
79 
80     serialize();
81 
82     // Emit deferred signal.
83     this->emit_object_added();
84     parent.startOrStopService(nslcdService, enabled());
85 }
86 
87 Config::Config(sdbusplus::bus::bus& bus, const char* path, const char* filePath,
88                const char* caCertFile, ConfigIface::Type lDAPType,
89                ConfigMgr& parent) :
90     Ifaces(bus, path, true),
91     tlsCacertFile(caCertFile), configFilePath(filePath), objectPath(path),
92     bus(bus), parent(parent)
93 {
94     ConfigIface::lDAPType(lDAPType);
95 
96     configPersistPath = parent.dbusPersistentPath;
97     configPersistPath += objectPath;
98 
99     // create the persistent directory
100     fs::create_directories(configPersistPath);
101 
102     configPersistPath += "/config";
103 
104     std::ofstream os(configPersistPath, std::ios::binary | std::ios::out);
105     // remove the read permission from others
106     auto permission =
107         fs::perms::owner_read | fs::perms::owner_write | fs::perms::group_read;
108     fs::permissions(configPersistPath, permission);
109 }
110 
111 void Config::writeConfig()
112 {
113     std::stringstream confData;
114     auto isPwdTobeWritten = false;
115     std::string userNameAttr;
116 
117     confData << "uid root\n";
118     confData << "gid root\n\n";
119     confData << "ldap_version 3\n\n";
120     confData << "timelimit 30\n";
121     confData << "bind_timelimit 30\n";
122     confData << "pagesize 1000\n";
123     confData << "referrals off\n\n";
124     confData << "uri " << lDAPServerURI() << "\n\n";
125     confData << "base " << lDAPBaseDN() << "\n\n";
126     confData << "binddn " << lDAPBindDN() << "\n";
127     if (!lDAPBindPassword.empty())
128     {
129         confData << "bindpw " << lDAPBindPassword << "\n";
130         isPwdTobeWritten = true;
131     }
132     confData << "\n";
133     switch (lDAPSearchScope())
134     {
135         case ConfigIface::SearchScope::sub:
136             confData << "scope sub\n\n";
137             break;
138         case ConfigIface::SearchScope::one:
139             confData << "scope one\n\n";
140             break;
141         case ConfigIface::SearchScope::base:
142             confData << "scope base\n\n";
143             break;
144     }
145     confData << "base passwd " << lDAPBaseDN() << "\n";
146     confData << "base shadow " << lDAPBaseDN() << "\n\n";
147     if (secureLDAP == true)
148     {
149         confData << "ssl on\n";
150         confData << "tls_reqcert hard\n";
151         confData << "tls_cacertFile " << tlsCacertFile.c_str() << "\n";
152     }
153     else
154     {
155         confData << "ssl off\n";
156     }
157     confData << "\n";
158     if (lDAPType() == ConfigIface::Type::ActiveDirectory)
159     {
160         if (ConfigIface::userNameAttribute().empty())
161         {
162             ConfigIface::userNameAttribute("sAMAccountName");
163         }
164         if (ConfigIface::groupNameAttribute().empty())
165         {
166             ConfigIface::groupNameAttribute("primaryGroupID");
167         }
168         confData << "filter passwd (&(objectClass=user)(objectClass=person)"
169                     "(!(objectClass=computer)))\n";
170         confData
171             << "filter group (|(objectclass=group)(objectclass=groupofnames) "
172                "(objectclass=groupofuniquenames))\n";
173         confData << "map passwd uid              "
174                  << ConfigIface::userNameAttribute() << "\n";
175         confData << "map passwd uidNumber        "
176                     "objectSid:S-1-5-21-3623811015-3361044348-30300820\n";
177         confData << "map passwd gidNumber        "
178                  << ConfigIface::groupNameAttribute() << "\n";
179         confData << "map passwd homeDirectory    \"/home/$sAMAccountName\"\n";
180         confData << "map passwd gecos            displayName\n";
181         confData << "map passwd loginShell       \"/bin/bash\"\n";
182         confData << "map group gidNumber         "
183                     "objectSid:S-1-5-21-3623811015-3361044348-30300820\n";
184         confData << "map group cn                "
185                  << ConfigIface::userNameAttribute() << "\n";
186     }
187     else if (lDAPType() == ConfigIface::Type::OpenLdap)
188     {
189         if (ConfigIface::userNameAttribute().empty())
190         {
191             ConfigIface::userNameAttribute("cn");
192         }
193         if (ConfigIface::groupNameAttribute().empty())
194         {
195             ConfigIface::groupNameAttribute("gidNumber");
196         }
197         confData << "filter passwd (objectclass=*)\n";
198         confData << "map passwd gecos displayName\n";
199         confData << "filter group (objectclass=posixGroup)\n";
200         confData << "map passwd uid              "
201                  << ConfigIface::userNameAttribute() << "\n";
202         confData << "map passwd gidNumber        "
203                  << ConfigIface::groupNameAttribute() << "\n";
204     }
205     try
206     {
207         std::fstream stream(configFilePath.c_str(), std::fstream::out);
208         // remove the read permission from others if password is being written.
209         // nslcd forces this behaviour.
210         auto permission = fs::perms::owner_read | fs::perms::owner_write |
211                           fs::perms::group_read;
212         if (isPwdTobeWritten)
213         {
214             fs::permissions(configFilePath, permission);
215         }
216         else
217         {
218             fs::permissions(configFilePath,
219                             permission | fs::perms::others_read);
220         }
221 
222         stream << confData.str();
223         stream.flush();
224         stream.close();
225     }
226     catch (const std::exception& e)
227     {
228         log<level::ERR>(e.what());
229         elog<InternalFailure>();
230     }
231     return;
232 }
233 
234 std::string Config::lDAPBindDNPassword(std::string value)
235 {
236     // Don't update the D-bus object, this is just to
237     // facilitate if user wants to change the bind dn password
238     // once d-bus object gets created.
239     lDAPBindPassword = value;
240     try
241     {
242         if (enabled())
243         {
244             writeConfig();
245             parent.startOrStopService(nslcdService, enabled());
246         }
247         serialize();
248     }
249     catch (const InternalFailure& e)
250     {
251         throw;
252     }
253     catch (const std::exception& e)
254     {
255         log<level::ERR>(e.what());
256         elog<InternalFailure>();
257     }
258     return value;
259 }
260 
261 std::string Config::lDAPServerURI(std::string value)
262 {
263     std::string val;
264     try
265     {
266         if (value == lDAPServerURI())
267         {
268             return value;
269         }
270         if (isValidLDAPURI(value, LDAPSscheme))
271         {
272             secureLDAP = true;
273         }
274         else if (isValidLDAPURI(value, LDAPscheme))
275         {
276             secureLDAP = false;
277         }
278         else
279         {
280             log<level::ERR>("bad LDAP Server URI",
281                             entry("LDAPSERVERURI=%s", value.c_str()));
282             elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPServerURI"),
283                                   Argument::ARGUMENT_VALUE(value.c_str()));
284         }
285 
286         if (secureLDAP && !fs::exists(tlsCacertFile.c_str()))
287         {
288             log<level::ERR>("LDAP server's CA certificate not provided",
289                             entry("TLSCACERTFILE=%s", tlsCacertFile.c_str()));
290             elog<NoCACertificate>();
291         }
292         val = ConfigIface::lDAPServerURI(value);
293         if (enabled())
294         {
295             writeConfig();
296             parent.startOrStopService(nslcdService, enabled());
297         }
298         // save the object.
299         serialize();
300     }
301     catch (const InternalFailure& e)
302     {
303         throw;
304     }
305     catch (const InvalidArgument& e)
306     {
307         throw;
308     }
309     catch (const NoCACertificate& e)
310     {
311         throw;
312     }
313     catch (const std::exception& e)
314     {
315         log<level::ERR>(e.what());
316         elog<InternalFailure>();
317     }
318     return val;
319 }
320 
321 std::string Config::lDAPBindDN(std::string value)
322 {
323     std::string val;
324     try
325     {
326         if (value == lDAPBindDN())
327         {
328             return value;
329         }
330 
331         if (value.empty())
332         {
333             log<level::ERR>("Not a valid LDAP BINDDN",
334                             entry("LDAPBINDDN=%s", value.c_str()));
335             elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPBindDN"),
336                                   Argument::ARGUMENT_VALUE(value.c_str()));
337         }
338 
339         val = ConfigIface::lDAPBindDN(value);
340         if (enabled())
341         {
342             writeConfig();
343             parent.startOrStopService(nslcdService, enabled());
344         }
345         // save the object.
346         serialize();
347     }
348     catch (const InternalFailure& e)
349     {
350         throw;
351     }
352     catch (const InvalidArgument& e)
353     {
354         throw;
355     }
356     catch (const std::exception& e)
357     {
358         log<level::ERR>(e.what());
359         elog<InternalFailure>();
360     }
361     return val;
362 }
363 
364 std::string Config::lDAPBaseDN(std::string value)
365 {
366     std::string val;
367     try
368     {
369         if (value == lDAPBaseDN())
370         {
371             return value;
372         }
373 
374         if (value.empty())
375         {
376             log<level::ERR>("Not a valid LDAP BASEDN",
377                             entry("BASEDN=%s", value.c_str()));
378             elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPBaseDN"),
379                                   Argument::ARGUMENT_VALUE(value.c_str()));
380         }
381 
382         val = ConfigIface::lDAPBaseDN(value);
383         if (enabled())
384         {
385             writeConfig();
386             parent.startOrStopService(nslcdService, enabled());
387         }
388         // save the object.
389         serialize();
390     }
391     catch (const InternalFailure& e)
392     {
393         throw;
394     }
395     catch (const InvalidArgument& e)
396     {
397         throw;
398     }
399     catch (const std::exception& e)
400     {
401         log<level::ERR>(e.what());
402         elog<InternalFailure>();
403     }
404     return val;
405 }
406 
407 ConfigIface::SearchScope Config::lDAPSearchScope(ConfigIface::SearchScope value)
408 {
409     ConfigIface::SearchScope val;
410     try
411     {
412         if (value == lDAPSearchScope())
413         {
414             return value;
415         }
416 
417         val = ConfigIface::lDAPSearchScope(value);
418         if (enabled())
419         {
420             writeConfig();
421 
422             parent.startOrStopService(nslcdService, enabled());
423         }
424         // save the object.
425         serialize();
426     }
427     catch (const InternalFailure& e)
428     {
429         throw;
430     }
431     catch (const std::exception& e)
432     {
433         log<level::ERR>(e.what());
434         elog<InternalFailure>();
435     }
436     return val;
437 }
438 
439 ConfigIface::Type Config::lDAPType(ConfigIface::Type value)
440 {
441     elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
442     return lDAPType();
443 }
444 
445 bool Config::enabled(bool value)
446 {
447     bool isEnable;
448     try
449     {
450         if (value == enabled())
451         {
452             return value;
453         }
454         isEnable = EnableIface::enabled(value);
455         if (isEnable)
456         {
457             writeConfig();
458         }
459         // TODO in later commit, one of the config would be active
460         // at any moment of time.
461         parent.startOrStopService(nslcdService, value);
462         // save the object.
463         serialize();
464     }
465     catch (const InternalFailure& e)
466     {
467         throw;
468     }
469     catch (const std::exception& e)
470     {
471         log<level::ERR>(e.what());
472         elog<InternalFailure>();
473     }
474     return isEnable;
475 }
476 
477 std::string Config::userNameAttribute(std::string value)
478 {
479     std::string val;
480     try
481     {
482         if (value == userNameAttribute())
483         {
484             return value;
485         }
486 
487         val = ConfigIface::userNameAttribute(value);
488         if (enabled())
489         {
490             writeConfig();
491 
492             parent.startOrStopService(nslcdService, enabled());
493         }
494         // save the object.
495         serialize();
496     }
497     catch (const InternalFailure& e)
498     {
499         throw;
500     }
501     catch (const std::exception& e)
502     {
503         log<level::ERR>(e.what());
504         elog<InternalFailure>();
505     }
506     return val;
507 }
508 
509 std::string Config::groupNameAttribute(std::string value)
510 {
511     std::string val;
512     try
513     {
514         if (value == groupNameAttribute())
515         {
516             return value;
517         }
518 
519         val = ConfigIface::groupNameAttribute(value);
520         if (enabled())
521         {
522             writeConfig();
523 
524             parent.startOrStopService(nslcdService, enabled());
525         }
526         // save the object.
527         serialize();
528     }
529     catch (const InternalFailure& e)
530     {
531         throw;
532     }
533     catch (const std::exception& e)
534     {
535         log<level::ERR>(e.what());
536         elog<InternalFailure>();
537     }
538     return val;
539 }
540 
541 template <class Archive>
542 void Config::save(Archive& archive, const std::uint32_t version) const
543 {
544     archive(this->enabled());
545     archive(lDAPServerURI());
546     archive(lDAPBindDN());
547     archive(lDAPBaseDN());
548     archive(lDAPSearchScope());
549     archive(lDAPBindPassword);
550     archive(userNameAttribute());
551     archive(groupNameAttribute());
552 }
553 
554 template <class Archive>
555 void Config::load(Archive& archive, const std::uint32_t version)
556 {
557 
558     bool bVal;
559     archive(bVal);
560     EnableIface::enabled(bVal);
561 
562     std::string str;
563     archive(str);
564     ConfigIface::lDAPServerURI(str);
565 
566     archive(str);
567     ConfigIface::lDAPBindDN(str);
568 
569     archive(str);
570     ConfigIface::lDAPBaseDN(str);
571 
572     ConfigIface::SearchScope scope;
573     archive(scope);
574     ConfigIface::lDAPSearchScope(scope);
575 
576     archive(str);
577     lDAPBindPassword = str;
578 
579     archive(str);
580     ConfigIface::userNameAttribute(str);
581 
582     archive(str);
583     ConfigIface::groupNameAttribute(str);
584 }
585 
586 void Config::serialize()
587 {
588     std::ofstream os(configPersistPath.string(),
589                      std::ios::binary | std::ios::out);
590     cereal::BinaryOutputArchive oarchive(os);
591     oarchive(*this);
592     return;
593 }
594 
595 bool Config::deserialize()
596 {
597     try
598     {
599         if (fs::exists(configPersistPath))
600         {
601             std::ifstream is(configPersistPath.c_str(),
602                              std::ios::in | std::ios::binary);
603             cereal::BinaryInputArchive iarchive(is);
604             iarchive(*this);
605             return true;
606         }
607         return false;
608     }
609     catch (cereal::Exception& e)
610     {
611         log<level::ERR>(e.what());
612         std::error_code ec;
613         fs::remove(configPersistPath, ec);
614         return false;
615     }
616     catch (const fs::filesystem_error& e)
617     {
618         return false;
619     }
620 }
621 
622 } // namespace ldap
623 } // namespace phosphor
624