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