1 #include "ldap_config_mgr.hpp" 2 #include "ldap_config.hpp" 3 #include "ldap_config_serialize.hpp" 4 5 #include "utils.hpp" 6 #include <filesystem> 7 #include <fstream> 8 #include <sstream> 9 10 namespace phosphor 11 { 12 namespace ldap 13 { 14 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 void ConfigMgr::startOrStopService(const std::string& service, bool start) 30 { 31 if (start) 32 { 33 restartService(service); 34 } 35 else 36 { 37 stopService(service); 38 } 39 } 40 41 void ConfigMgr::restartService(const std::string& service) 42 { 43 try 44 { 45 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 46 SYSTEMD_INTERFACE, "RestartUnit"); 47 method.append(service.c_str(), "replace"); 48 bus.call_noreply(method); 49 } 50 catch (const sdbusplus::exception::SdBusError& ex) 51 { 52 log<level::ERR>("Failed to restart service", 53 entry("SERVICE=%s", service.c_str()), 54 entry("ERR=%s", ex.what())); 55 elog<InternalFailure>(); 56 } 57 } 58 void ConfigMgr::stopService(const std::string& service) 59 { 60 try 61 { 62 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 63 SYSTEMD_INTERFACE, "StopUnit"); 64 method.append(service.c_str(), "replace"); 65 bus.call_noreply(method); 66 } 67 catch (const sdbusplus::exception::SdBusError& ex) 68 { 69 log<level::ERR>("Failed to stop service", 70 entry("SERVICE=%s", service.c_str()), 71 entry("ERR=%s", ex.what())); 72 elog<InternalFailure>(); 73 } 74 } 75 76 void ConfigMgr::deleteObject() 77 { 78 configPtr.reset(nullptr); 79 } 80 81 std::string ConfigMgr::createConfig( 82 std::string lDAPServerURI, std::string lDAPBindDN, std::string lDAPBaseDN, 83 std::string lDAPBindDNPassword, CreateIface::SearchScope lDAPSearchScope, 84 CreateIface::Create::Type lDAPType, std::string groupNameAttribute, 85 std::string userNameAttribute) 86 { 87 bool secureLDAP = false; 88 89 if (isValidLDAPURI(lDAPServerURI, LDAPSscheme)) 90 { 91 secureLDAP = true; 92 } 93 else if (isValidLDAPURI(lDAPServerURI, LDAPscheme)) 94 { 95 secureLDAP = false; 96 } 97 else 98 { 99 log<level::ERR>("bad LDAP Server URI", 100 entry("LDAPSERVERURI=%s", lDAPServerURI.c_str())); 101 elog<InvalidArgument>(Argument::ARGUMENT_NAME("lDAPServerURI"), 102 Argument::ARGUMENT_VALUE(lDAPServerURI.c_str())); 103 } 104 105 if (secureLDAP && !fs::exists(tlsCacertFile.c_str())) 106 { 107 log<level::ERR>("LDAP server's CA certificate not provided", 108 entry("TLSCACERTFILE=%s", tlsCacertFile.c_str())); 109 elog<NoCACertificate>(); 110 } 111 112 if (lDAPBindDN.empty()) 113 { 114 log<level::ERR>("Not a valid LDAP BINDDN", 115 entry("LDAPBINDDN=%s", lDAPBindDN.c_str())); 116 elog<InvalidArgument>(Argument::ARGUMENT_NAME("LDAPBindDN"), 117 Argument::ARGUMENT_VALUE(lDAPBindDN.c_str())); 118 } 119 120 if (lDAPBaseDN.empty()) 121 { 122 log<level::ERR>("Not a valid LDAP BASEDN", 123 entry("LDAPBASEDN=%s", lDAPBaseDN.c_str())); 124 elog<InvalidArgument>(Argument::ARGUMENT_NAME("LDAPBaseDN"), 125 Argument::ARGUMENT_VALUE(lDAPBaseDN.c_str())); 126 } 127 128 // With current implementation we support only one LDAP server. 129 deleteObject(); 130 131 auto objPath = std::string(LDAP_CONFIG_DBUS_OBJ_PATH); 132 configPtr = std::make_unique<Config>( 133 bus, objPath.c_str(), configFilePath.c_str(), tlsCacertFile.c_str(), 134 secureLDAP, lDAPServerURI, lDAPBindDN, lDAPBaseDN, 135 std::move(lDAPBindDNPassword), 136 static_cast<ConfigIface::SearchScope>(lDAPSearchScope), 137 static_cast<ConfigIface::Type>(lDAPType), false, groupNameAttribute, 138 userNameAttribute, *this); 139 140 restartService(nscdService); 141 return objPath; 142 } 143 144 void ConfigMgr::restore(const char* filePath) 145 { 146 if (!fs::exists(filePath)) 147 { 148 log<level::ERR>("Config file doesn't exists", 149 entry("LDAP_CONFIG_FILE=%s", configFilePath.c_str())); 150 return; 151 } 152 153 ConfigInfo configValues; 154 try 155 { 156 std::fstream stream(filePath, std::fstream::in); 157 Line line; 158 // read characters from stream and places them into line 159 while (std::getline(stream, line)) 160 { 161 // remove leading and trailing extra spaces 162 auto firstScan = line.find_first_not_of(' '); 163 auto first = 164 (firstScan == std::string::npos ? line.length() : firstScan); 165 auto last = line.find_last_not_of(' '); 166 line = line.substr(first, last - first + 1); 167 // reduce multiple spaces between two words to a single space 168 auto pred = [](char a, char b) { 169 return (a == b && a == ' ') ? true : false; 170 }; 171 172 auto lastPos = std::unique(line.begin(), line.end(), pred); 173 174 line.erase(lastPos, line.end()); 175 176 // Ignore if line is empty or starts with '#' 177 if (line.empty() || line.at(0) == '#') 178 { 179 continue; 180 } 181 182 Key key; 183 std::istringstream isLine(line); 184 // extract characters from isLine and stores them into 185 // key until the delimitation character ' ' is found. 186 // If the delimiter is found, it is extracted and discarded 187 // the next input operation will begin after it. 188 if (std::getline(isLine, key, ' ')) 189 { 190 Val value; 191 // extract characters after delimitation character ' ' 192 if (std::getline(isLine, value, ' ')) 193 { 194 // skip line if it starts with "base shadow" or 195 // "base passwd" because we would have 3 entries 196 // ("base lDAPBaseDN" , "base passwd lDAPBaseDN" and 197 // "base shadow lDAPBaseDN") for the property "lDAPBaseDN", 198 // one is enough to restore it. 199 200 if ((key == "base") && 201 (value == "passwd" || value == "shadow")) 202 { 203 continue; 204 } 205 206 // if config type is AD "map group" entry would be add to 207 // the map configValues. For OpenLdap config file no map 208 // entry would be there. 209 if ((key == "map") && (value == "passwd")) 210 { 211 key = key + "_" + value; 212 if (std::getline(isLine, value, ' ')) 213 { 214 key += "_" + value; 215 } 216 std::getline(isLine, value, ' '); 217 } 218 configValues[key] = value; 219 } 220 } 221 } 222 223 CreateIface::SearchScope lDAPSearchScope; 224 if (configValues["scope"] == "sub") 225 { 226 lDAPSearchScope = CreateIface::SearchScope::sub; 227 } 228 else if (configValues["scope"] == "one") 229 { 230 lDAPSearchScope = CreateIface::SearchScope::one; 231 } 232 else 233 { 234 lDAPSearchScope = CreateIface::SearchScope::base; 235 } 236 237 CreateIface::Type lDAPType; 238 // If the file is having a line which starts with "map group" 239 if (configValues["map"] == "group") 240 { 241 lDAPType = CreateIface::Type::ActiveDirectory; 242 } 243 else 244 { 245 lDAPType = CreateIface::Type::OpenLdap; 246 } 247 248 // Don't create the config object if either of the field is empty. 249 if (configValues["uri"] == "" || configValues["binddn"] == "" || 250 configValues["base"] == "") 251 { 252 log<level::INFO>( 253 "LDAP config parameter value missing", 254 entry("URI=%s", configValues["uri"].c_str()), 255 entry("BASEDN=%s", configValues["base"].c_str()), 256 entry("BINDDN=%s", configValues["binddn"].c_str())); 257 return; 258 } 259 260 createConfig(std::move(configValues["uri"]), 261 std::move(configValues["binddn"]), 262 std::move(configValues["base"]), 263 std::move(configValues["bindpw"]), lDAPSearchScope, 264 lDAPType, std::move(configValues["map_passwd_uid"]), 265 std::move(configValues["map_passwd_gidNumber"])); 266 267 // Get the enabled property value from the persistent location 268 if (!deserialize(dbusPersistentPath, *configPtr)) 269 { 270 log<level::INFO>( 271 "Deserialization Failed, continue with service disable"); 272 } 273 } 274 catch (const InvalidArgument& e) 275 { 276 // Don't throw - we don't want to create a D-Bus 277 // object upon finding empty values in config, as 278 // this can be a default config. 279 } 280 catch (const NoCACertificate& e) 281 { 282 // Don't throw - we don't want to create a D-Bus 283 // object upon finding "ssl on" without having tls_cacertFile in place, 284 // as this can be a default config. 285 } 286 catch (const InternalFailure& e) 287 { 288 throw; 289 } 290 catch (const std::exception& e) 291 { 292 log<level::ERR>(e.what()); 293 elog<InternalFailure>(); 294 } 295 } 296 } // namespace ldap 297 } // namespace phosphor 298