blob: e3b337d46dff841a69644b8961fec7c175d02c31 [file] [log] [blame]
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -05001#include "ldap_configuration.hpp"
Nagaraju Gorugantib26799a2018-09-28 13:12:19 -05002#include <ldap.h>
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -05003#include <experimental/filesystem>
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -05004#include <fstream>
5#include <sstream>
6
7namespace phosphor
8{
9namespace ldap
10{
11constexpr auto nslcdService = "nslcd.service";
Nagaraju Gorugantidccee2b2018-09-25 08:51:06 -050012constexpr auto nscdService = "nscd.service";
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -050013
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -050014using namespace phosphor::logging;
15using namespace sdbusplus::xyz::openbmc_project::Common::Error;
16namespace fs = std::experimental::filesystem;
Nagaraju Gorugantib26799a2018-09-28 13:12:19 -050017using Argument = xyz::openbmc_project::Common::InvalidArgument;
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -050018
19using Line = std::string;
20using Key = std::string;
21using Val = std::string;
22using ConfigInfo = std::map<Key, Val>;
23
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -050024Config::Config(sdbusplus::bus::bus& bus, const char* path, const char* filePath,
25 bool secureLDAP, std::string lDAPServerURI,
26 std::string lDAPBindDN, std::string lDAPBaseDN,
27 std::string lDAPBindDNpassword,
28 ldap_base::Config::SearchScope lDAPSearchScope,
29 ldap_base::Config::Type lDAPType, ConfigMgr& parent) :
30 ConfigIface(bus, path, true),
31 configFilePath(filePath), bus(bus), parent(parent)
32{
33 ConfigIface::secureLDAP(secureLDAP);
34 ConfigIface::lDAPServerURI(lDAPServerURI);
35 ConfigIface::lDAPBindDN(lDAPBindDN);
36 ConfigIface::lDAPBaseDN(lDAPBaseDN);
37 ConfigIface::lDAPBINDDNpassword(lDAPBindDNpassword);
38 ConfigIface::lDAPSearchScope(lDAPSearchScope);
39 ConfigIface::lDAPType(lDAPType);
40 writeConfig();
41 parent.restartService(nslcdService);
42 // Emit deferred signal.
43 this->emit_object_added();
44}
45
Nagaraju Goruganti24194bd2018-09-18 09:55:09 -050046void Config::delete_()
47{
48 parent.deleteObject();
Nagaraju Gorugantidccee2b2018-09-25 08:51:06 -050049 try
50 {
51 fs::copy_file(defaultNslcdFile, LDAP_CONFIG_FILE,
52 fs::copy_options::overwrite_existing);
53 fs::copy_file(nsSwitchFile, LDAPNsSwitchFile,
54 fs::copy_options::overwrite_existing);
55 fs::copy_file(linuxNsSwitchFile, nsSwitchFile,
56 fs::copy_options::overwrite_existing);
57 }
58 catch (const std::exception& e)
59 {
60 log<level::ERR>("Failed to rename Config Files while deleting Object",
61 entry("ERR=%s", e.what()));
62 elog<InternalFailure>();
63 }
64
65 parent.restartService(nscdService);
66 parent.stopService(nslcdService);
Nagaraju Goruganti24194bd2018-09-18 09:55:09 -050067}
68
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -050069void Config::writeConfig()
70{
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -050071 std::stringstream confData;
72 confData << "uid root\n";
73 confData << "gid root\n\n";
74 confData << "ldap_version 3\n\n";
75 confData << "timelimit 30\n";
76 confData << "bind_timelimit 30\n";
77 confData << "pagesize 1000\n";
78 confData << "referrals off\n\n";
79 confData << "uri " << lDAPServerURI() << "\n\n";
80 confData << "base " << lDAPBaseDN() << "\n\n";
81 confData << "binddn " << lDAPBindDN() << "\n";
82 confData << "bindpw " << lDAPBINDDNpassword() << "\n\n";
83 switch (lDAPSearchScope())
84 {
85 case ldap_base::Config::SearchScope::sub:
86 confData << "scope sub\n\n";
87 break;
88 case ldap_base::Config::SearchScope::one:
89 confData << "scope one\n\n";
90 break;
91 case ldap_base::Config::SearchScope::base:
92 confData << "scope base\n\n";
93 break;
94 }
95 confData << "base passwd " << lDAPBaseDN() << "\n";
96 confData << "base shadow " << lDAPBaseDN() << "\n\n";
97 if (secureLDAP() == true)
98 {
99 confData << "ssl on\n";
100 confData << "tls_reqcert allow\n";
101 confData << "tls_cert /etc/nslcd/certs/cert.pem\n";
102 }
103 else
104 {
105 confData << "ssl off\n\n";
106 }
107 if (lDAPType() == ldap_base::Config::Type::ActiveDirectory)
108 {
109 confData << "filter passwd (&(objectClass=user)(objectClass=person)"
110 "(!(objectClass=computer)))\n";
111 confData
112 << "filter group (|(objectclass=group)(objectclass=groupofnames) "
113 "(objectclass=groupofuniquenames))\n";
114 confData << "map passwd uid sAMAccountName\n";
115 confData << "map passwd uidNumber "
116 "objectSid:S-1-5-21-3623811015-3361044348-30300820\n";
117 confData << "map passwd gidNumber primaryGroupID\n";
118 confData << "map passwd homeDirectory \"/home/$sAMAccountName\"\n";
119 confData << "map passwd gecos displayName\n";
120 confData << "map passwd loginShell \"/bin/bash\"\n";
121 confData << "map group gidNumber primaryGroupID\n";
122 confData << "map group gidNumber "
123 "objectSid:S-1-5-21-3623811015-3361044348-30300820\n";
124 confData << "map group cn sAMAccountName\n";
125 }
126 else if (lDAPType() == ldap_base::Config::Type::OpenLdap)
127 {
128 confData << "filter passwd (objectclass=*)\n";
129 confData << "map passwd uid cn\n";
130 confData << "map passwd gecos displayName\n";
131 }
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500132 try
133 {
134 std::fstream stream(configFilePath.c_str(), std::fstream::out);
135 stream << confData.str();
136 stream.flush();
137 stream.close();
138 }
139 catch (const std::exception& e)
140 {
141 log<level::ERR>(e.what());
142 elog<InternalFailure>();
143 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500144 return;
145}
146
147bool Config::secureLDAP(bool value)
148{
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500149 bool val = false;
150 try
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500151 {
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500152 if (value == secureLDAP())
153 {
154 return value;
155 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500156
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500157 val = ConfigIface::secureLDAP(value);
158 writeConfig();
159 parent.restartService(nslcdService);
160 }
161 catch (const InternalFailure& e)
162 {
163 throw;
164 }
165 catch (const std::exception& e)
166 {
167 log<level::ERR>(e.what());
168 elog<InternalFailure>();
169 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500170
171 return val;
172}
173
174std::string Config::lDAPServerURI(std::string value)
175{
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500176 std::string val;
177 try
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500178 {
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500179 if (value == lDAPServerURI())
180 {
181 return value;
182 }
Nagaraju Gorugantib26799a2018-09-28 13:12:19 -0500183 if (!(ldap_is_ldap_url(value.c_str()) ||
184 ldap_is_ldaps_url(value.c_str())))
185 {
186 log<level::ERR>("Not a valid LDAP Server URI"),
187 entry("LDAPSERVERURI=%s", value.c_str());
188 elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPServerURI"),
189 Argument::ARGUMENT_VALUE(value.c_str()));
190 }
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500191 val = ConfigIface::lDAPServerURI(value);
192 writeConfig();
193 parent.restartService(nslcdService);
194 }
195 catch (const InternalFailure& e)
196 {
197 throw;
198 }
199 catch (const std::exception& e)
200 {
201 log<level::ERR>(e.what());
202 elog<InternalFailure>();
203 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500204
205 return val;
206}
207
208std::string Config::lDAPBindDN(std::string value)
209{
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500210 std::string val;
211 try
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500212 {
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500213 if (value == lDAPBindDN())
214 {
215 return value;
216 }
217
Nagaraju Gorugantib26799a2018-09-28 13:12:19 -0500218 if (value.empty())
219 {
220 log<level::ERR>("Not a valid LDAP BINDDN"),
221 entry("LDAPBINDDN=%s", value.c_str());
222 elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPBindDN"),
223 Argument::ARGUMENT_VALUE(value.c_str()));
224 }
225
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500226 val = ConfigIface::lDAPBindDN(value);
227 writeConfig();
228 parent.restartService(nslcdService);
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500229 }
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500230 catch (const InternalFailure& e)
231 {
232 throw;
233 }
234 catch (const std::exception& e)
235 {
236 log<level::ERR>(e.what());
237 elog<InternalFailure>();
238 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500239 return val;
240}
241
242std::string Config::lDAPBaseDN(std::string value)
243{
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500244 std::string val;
245 try
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500246 {
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500247 if (value == lDAPBaseDN())
248 {
249 return value;
250 }
251
Nagaraju Gorugantib26799a2018-09-28 13:12:19 -0500252 if (value.empty())
253 {
254 log<level::ERR>("Not a valid LDAP BASEDN"),
255 entry("BASEDN=%s", value.c_str());
256 elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPBaseDN"),
257 Argument::ARGUMENT_VALUE(value.c_str()));
258 }
259
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500260 val = ConfigIface::lDAPBaseDN(value);
261 writeConfig();
262 parent.restartService(nslcdService);
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500263 }
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500264 catch (const InternalFailure& e)
265 {
266 throw;
267 }
268 catch (const std::exception& e)
269 {
270 log<level::ERR>(e.what());
271 elog<InternalFailure>();
272 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500273 return val;
274}
275
276std::string Config::lDAPBINDDNpassword(std::string value)
277{
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500278 std::string val;
279 try
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500280 {
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500281 if (value == lDAPBINDDNpassword())
282 {
283 return value;
284 }
285
286 val = ConfigIface::lDAPBINDDNpassword(value);
287 writeConfig();
288 parent.restartService(nslcdService);
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500289 }
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500290 catch (const InternalFailure& e)
291 {
292 throw;
293 }
294 catch (const std::exception& e)
295 {
296 log<level::ERR>(e.what());
297 elog<InternalFailure>();
298 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500299 return val;
300}
301
302ldap_base::Config::SearchScope
303 Config::lDAPSearchScope(ldap_base::Config::SearchScope value)
304{
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500305 ldap_base::Config::SearchScope val;
306 try
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500307 {
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500308 if (value == lDAPSearchScope())
309 {
310 return value;
311 }
312
313 val = ConfigIface::lDAPSearchScope(value);
314 writeConfig();
315 parent.restartService(nslcdService);
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500316 }
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500317 catch (const InternalFailure& e)
318 {
319 throw;
320 }
321 catch (const std::exception& e)
322 {
323 log<level::ERR>(e.what());
324 elog<InternalFailure>();
325 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500326 return val;
327}
328
329ldap_base::Config::Type Config::lDAPType(ldap_base::Config::Type value)
330{
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500331 ldap_base::Config::Type val;
332 try
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500333 {
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500334 if (value == lDAPType())
335 {
336 return value;
337 }
338
339 val = ConfigIface::lDAPType(value);
340 writeConfig();
341 parent.restartService(nslcdService);
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500342 }
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500343 catch (const InternalFailure& e)
344 {
345 throw;
346 }
347 catch (const std::exception& e)
348 {
349 log<level::ERR>(e.what());
350 elog<InternalFailure>();
351 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500352 return val;
353}
354
355void ConfigMgr::restartService(const std::string& service)
356{
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500357 try
358 {
359 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
360 SYSTEMD_INTERFACE, "RestartUnit");
361 method.append(service.c_str(), "replace");
362 bus.call_noreply(method);
363 }
364 catch (const sdbusplus::exception::SdBusError& ex)
365 {
366 log<level::ERR>("Failed to restart nslcd service",
367 entry("ERR=%s", ex.what()));
368 elog<InternalFailure>();
369 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500370}
371
Nagaraju Gorugantidccee2b2018-09-25 08:51:06 -0500372void ConfigMgr::stopService(const std::string& service)
373{
374 try
375 {
376 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
377 SYSTEMD_INTERFACE, "StopUnit");
378 method.append(service.c_str(), "replace");
379 bus.call_noreply(method);
380 }
381 catch (const sdbusplus::exception::SdBusError& ex)
382 {
383 log<level::ERR>("Failed to stop nslcd service",
384 entry("ERR=%s", ex.what()));
385 elog<InternalFailure>();
386 }
387}
388
Nagaraju Goruganti24194bd2018-09-18 09:55:09 -0500389void ConfigMgr::deleteObject()
390{
391 configPtr.reset(nullptr);
392}
393
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500394std::string
395 ConfigMgr::createConfig(bool secureLDAP, std::string lDAPServerURI,
396 std::string lDAPBindDN, std::string lDAPBaseDN,
397 std::string lDAPBINDDNpassword,
398 ldap_base::Create::SearchScope lDAPSearchScope,
399 ldap_base::Create::Type lDAPType)
400{
Nagaraju Gorugantib26799a2018-09-28 13:12:19 -0500401 if (!(ldap_is_ldap_url(lDAPServerURI.c_str()) ||
402 ldap_is_ldaps_url(lDAPServerURI.c_str())))
403 {
404 log<level::ERR>("Not a valid LDAP Server URI"),
405 entry("LDAPSERVERURI=%s", lDAPServerURI.c_str());
406 elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPServerURI"),
407 Argument::ARGUMENT_VALUE(lDAPServerURI.c_str()));
408 }
409
410 if (lDAPBindDN.empty())
411 {
412 log<level::ERR>("Not a valid LDAP BINDDN"),
413 entry("LDAPBINDDN=%s", lDAPBindDN.c_str());
414 elog<InvalidArgument>(Argument::ARGUMENT_NAME("LDAPBindDN"),
415 Argument::ARGUMENT_VALUE(lDAPBindDN.c_str()));
416 }
417
418 if (lDAPBaseDN.empty())
419 {
420 log<level::ERR>("Not a valid LDAP BASEDN"),
421 entry("LDAPBASEDN=%s", lDAPBaseDN.c_str());
422 elog<InvalidArgument>(Argument::ARGUMENT_NAME("LDAPBaseDN"),
423 Argument::ARGUMENT_VALUE(lDAPBaseDN.c_str()));
424 }
425
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500426 // With current implementation we support only one LDAP server.
Nagaraju Goruganti24194bd2018-09-18 09:55:09 -0500427 deleteObject();
Nagaraju Gorugantidccee2b2018-09-25 08:51:06 -0500428 try
429 {
430 fs::copy_file(nsSwitchFile, linuxNsSwitchFile,
431 fs::copy_options::overwrite_existing);
432 fs::copy_file(LDAPNsSwitchFile, nsSwitchFile,
433 fs::copy_options::overwrite_existing);
434 }
435 catch (const std::exception& e)
436 {
437 log<level::ERR>("Failed to rename Config Files while creating Object",
438 entry("ERR=%s", e.what()));
439 elog<InternalFailure>();
440 }
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500441
442 auto objPath = std::string(LDAP_CONFIG_DBUS_OBJ_PATH);
443 configPtr = std::make_unique<Config>(
444 bus, objPath.c_str(), LDAP_CONFIG_FILE, secureLDAP, lDAPServerURI,
445 lDAPBindDN, lDAPBaseDN, lDAPBINDDNpassword,
446 static_cast<ldap_base::Config::SearchScope>(lDAPSearchScope),
447 static_cast<ldap_base::Config::Type>(lDAPType), *this);
448
Nagaraju Gorugantidccee2b2018-09-25 08:51:06 -0500449 restartService(nslcdService);
450 restartService(nscdService);
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500451 return objPath;
452}
453
Nagaraju Gorugantif1940d92018-09-18 05:05:50 -0500454void ConfigMgr::restore(const char* filePath)
455{
456 if (!fs::exists(filePath))
457 {
458 log<level::ERR>("Config file doesn't exists",
459 entry("LDAP_CONFIG_FILE=%s", LDAP_CONFIG_FILE));
460 return;
461 }
462
463 ConfigInfo configValues;
464
465 try
466 {
467 std::fstream stream(filePath, std::fstream::in);
468 Line line;
469 // read characters from stream and places them into line
470 while (std::getline(stream, line))
471 {
472 // remove leading and trailing extra spaces
473 auto firstScan = line.find_first_not_of(' ');
474 auto first =
475 (firstScan == std::string::npos ? line.length() : firstScan);
476 auto last = line.find_last_not_of(' ');
477 line = line.substr(first, last - first + 1);
478 // reduce multiple spaces between two words to a single space
479 auto pred = [](char a, char b) {
480 return (a == b && a == ' ') ? true : false;
481 };
482
483 auto lastPos = std::unique(line.begin(), line.end(), pred);
484
485 line.erase(lastPos, line.end());
486
487 // Ignore if line is empty or starts with '#'
488 if (line.empty() || line.at(0) == '#')
489 {
490 continue;
491 }
492
493 Key key;
494 std::istringstream isLine(line);
495 // extract characters from isLine and stores them into
496 // key until the delimitation character ' ' is found.
497 // If the delimiter is found, it is extracted and discarded
498 // the next input operation will begin after it.
499 if (std::getline(isLine, key, ' '))
500 {
501 Val value;
502 // extract characters after delimitation character ' '
503 if (std::getline(isLine, value, ' '))
504 {
505 // skip line if it starts with "base shadow" or
506 // "base passwd" because we would have 3 entries
507 // ("base lDAPBaseDN" , "base passwd lDAPBaseDN" and
508 // "base shadow lDAPBaseDN") for the property "lDAPBaseDN",
509 // one is enough to restore it.
510
511 if ((key == "base") &&
512 (value == "passwd" || value == "shadow"))
513 {
514 continue;
515 }
516 // skip the line if it starts with "map passwd".
517 // if config type is AD "map group" entry would be add to
518 // the map configValues. For OpenLdap config file no map
519 // entry would be there.
520 if ((key == "map") && (value == "passwd"))
521 {
522 continue;
523 }
524 configValues[key] = value;
525 }
526 }
527 }
528
529 // extract properties from configValues map
530 bool secureLDAP;
531 if (configValues["ssl"] == "on")
532 {
533 secureLDAP = true;
534 }
535 else
536 {
537 secureLDAP = false;
538 }
539
540 ldap_base::Create::SearchScope lDAPSearchScope;
541 if (configValues["scope"] == "sub")
542 {
543 lDAPSearchScope = ldap_base::Create::SearchScope::sub;
544 }
545 else if (configValues["scope"] == "one")
546 {
547 lDAPSearchScope = ldap_base::Create::SearchScope::one;
548 }
549 else
550 {
551 lDAPSearchScope = ldap_base::Create::SearchScope::base;
552 }
553
554 ldap_base::Create::Type lDAPType;
555 // If the file is having a line which starts with "map group"
556 if (configValues["map"] == "group")
557 {
558 lDAPType = ldap_base::Create::Type::ActiveDirectory;
559 }
560 else
561 {
562 lDAPType = ldap_base::Create::Type::OpenLdap;
563 }
564
565 createConfig(
566 secureLDAP, std::move(configValues["uri"]),
567 std::move(configValues["binddn"]), std::move(configValues["base"]),
568 std::move(configValues["bindpw"]), lDAPSearchScope, lDAPType);
569 }
570 catch (const InvalidArgument& e)
571 {
572 // Don't throw - we don't want to create a D-Bus
573 // object upon finding empty values in config, as
574 // this can be a default config.
575 }
576 catch (const InternalFailure& e)
577 {
578 throw;
579 }
580 catch (const std::exception& e)
581 {
582 log<level::ERR>(e.what());
583 elog<InternalFailure>();
584 }
585}
Nagaraju Goruganti997f5e02018-08-30 03:05:11 -0500586} // namespace ldap
587} // namespace phosphor