blob: 0a79ef2d6aad469684a4c317498e7f77e0e98cda [file] [log] [blame]
Ratan Guptae1f4db62019-04-11 18:57:42 +05301#include "ldap_config_mgr.hpp"
Ratan Gupta37fb3fe2019-04-13 12:54:18 +05302#include "ldap_config.hpp"
3#include "ldap_config_serialize.hpp"
Ratan Guptae1f4db62019-04-11 18:57:42 +05304
5#include "utils.hpp"
6#include <filesystem>
7#include <fstream>
8#include <sstream>
9
10namespace phosphor
11{
12namespace ldap
13{
14
15constexpr auto nscdService = "nscd.service";
16constexpr auto LDAPscheme = "ldap";
17constexpr auto LDAPSscheme = "ldaps";
18
19using namespace phosphor::logging;
20using namespace sdbusplus::xyz::openbmc_project::Common::Error;
21namespace fs = std::filesystem;
22using Argument = xyz::openbmc_project::Common::InvalidArgument;
23
24using Line = std::string;
25using Key = std::string;
26using Val = std::string;
27using ConfigInfo = std::map<Key, Val>;
28
29void 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
41void 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}
58void 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
76void ConfigMgr::deleteObject()
77{
78 configPtr.reset(nullptr);
79}
80
81std::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
144void 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