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