xref: /openbmc/phosphor-user-manager/users.cpp (revision 3ddb95af8f08638597a31dfa87d4d099a0dbd7d0)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "config.h"
18 
19 #include "users.hpp"
20 
21 #include "totp.hpp"
22 #include "user_mgr.hpp"
23 
24 #include <pwd.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28 
29 #include <phosphor-logging/elog-errors.hpp>
30 #include <phosphor-logging/elog.hpp>
31 #include <phosphor-logging/lg2.hpp>
32 #include <xyz/openbmc_project/Common/error.hpp>
33 #include <xyz/openbmc_project/User/Common/error.hpp>
34 
35 #include <filesystem>
36 #include <fstream>
37 #include <map>
38 namespace phosphor
39 {
40 namespace user
41 {
42 
43 using namespace phosphor::logging;
44 using InsufficientPermission =
45     sdbusplus::xyz::openbmc_project::Common::Error::InsufficientPermission;
46 using InternalFailure =
47     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
48 using InvalidArgument =
49     sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
50 using NoResource =
51     sdbusplus::xyz::openbmc_project::User::Common::Error::NoResource;
52 using UnsupportedRequest =
53     sdbusplus::xyz::openbmc_project::Common::Error::UnsupportedRequest;
54 
55 using Argument = xyz::openbmc_project::Common::InvalidArgument;
56 static constexpr auto authAppPath = "/usr/bin/google-authenticator";
57 static constexpr auto secretKeyPath = "/home/{}/.google_authenticator";
58 static constexpr auto secretKeyTempPath =
59     "/home/{}/.config/phosphor-user-manager/.google_authenticator.tmp";
60 
61 /** @brief Constructs UserMgr object.
62  *
63  *  @param[in] bus  - sdbusplus handler
64  *  @param[in] path - D-Bus path
65  *  @param[in] groups - users group list
66  *  @param[in] priv - user privilege
67  *  @param[in] enabled - user enabled state
68  *  @param[in] parent - user manager - parent object
69  */
Users(sdbusplus::bus_t & bus,const char * path,std::vector<std::string> groups,std::string priv,bool enabled,UserMgr & parent)70 Users::Users(sdbusplus::bus_t& bus, const char* path,
71              std::vector<std::string> groups, std::string priv, bool enabled,
72              UserMgr& parent) :
73     Interfaces(bus, path, Interfaces::action::defer_emit),
74     userName(sdbusplus::message::object_path(path).filename()), manager(parent)
75 {
76     UsersIface::userPrivilege(priv, true);
77     UsersIface::userGroups(groups, true);
78     UsersIface::userEnabled(enabled, true);
79     load(manager.getSerializer());
80     this->emit_object_added();
81 }
~Users()82 Users::~Users()
83 {
84     manager.getSerializer().erase(userName);
85 }
86 /** @brief delete user method.
87  *  This method deletes the user as requested
88  *
89  */
delete_(void)90 void Users::delete_(void)
91 {
92     manager.deleteUser(userName);
93 }
94 
95 /** @brief update user privilege
96  *
97  *  @param[in] value - User privilege
98  */
userPrivilege(std::string value)99 std::string Users::userPrivilege(std::string value)
100 {
101     if (value == UsersIface::userPrivilege())
102     {
103         return value;
104     }
105     manager.updateGroupsAndPriv(userName, UsersIface::userGroups(), value);
106     return UsersIface::userPrivilege(value);
107 }
108 
setUserPrivilege(const std::string & value)109 void Users::setUserPrivilege(const std::string& value)
110 {
111     UsersIface::userPrivilege(value);
112 }
113 
setUserGroups(const std::vector<std::string> & groups)114 void Users::setUserGroups(const std::vector<std::string>& groups)
115 {
116     UsersIface::userGroups(groups);
117 }
118 
119 /** @brief list user privilege
120  *
121  */
userPrivilege(void) const122 std::string Users::userPrivilege(void) const
123 {
124     return UsersIface::userPrivilege();
125 }
126 
127 /** @brief update user groups
128  *
129  *  @param[in] value - User groups
130  */
userGroups(std::vector<std::string> value)131 std::vector<std::string> Users::userGroups(std::vector<std::string> value)
132 {
133     if (value == UsersIface::userGroups())
134     {
135         return value;
136     }
137     std::sort(value.begin(), value.end());
138     manager.updateGroupsAndPriv(userName, value, UsersIface::userPrivilege());
139     return UsersIface::userGroups(value);
140 }
141 
142 /** @brief list user groups
143  *
144  */
userGroups(void) const145 std::vector<std::string> Users::userGroups(void) const
146 {
147     return UsersIface::userGroups();
148 }
149 
150 /** @brief lists user enabled state
151  *
152  */
userEnabled(void) const153 bool Users::userEnabled(void) const
154 {
155     return manager.isUserEnabled(userName);
156 }
157 
setUserEnabled(bool value)158 void Users::setUserEnabled(bool value)
159 {
160     UsersIface::userEnabled(value);
161 }
162 
163 /** @brief update user enabled state
164  *
165  *  @param[in] value - bool value
166  */
userEnabled(bool value)167 bool Users::userEnabled(bool value)
168 {
169     if (value == UsersIface::userEnabled())
170     {
171         return value;
172     }
173     manager.userEnable(userName, value);
174     return UsersIface::userEnabled(value);
175 }
176 
177 /** @brief lists user locked state for failed attempt
178  *
179  **/
userLockedForFailedAttempt(void) const180 bool Users::userLockedForFailedAttempt(void) const
181 {
182     return manager.userLockedForFailedAttempt(userName);
183 }
184 
185 /** @brief unlock user locked state for failed attempt
186  *
187  * @param[in]: value - false - unlock user account, true - no action taken
188  **/
userLockedForFailedAttempt(bool value)189 bool Users::userLockedForFailedAttempt(bool value)
190 {
191     if (value != false)
192     {
193         return userLockedForFailedAttempt();
194     }
195     else
196     {
197         return manager.userLockedForFailedAttempt(userName, value);
198     }
199 }
200 
201 /** @brief indicates if the user's password is expired
202  *
203  **/
userPasswordExpired(void) const204 bool Users::userPasswordExpired(void) const
205 {
206     return manager.userPasswordExpired(userName);
207 }
changeFileOwnership(const std::string & userName)208 bool changeFileOwnership(const std::string& userName)
209 {
210     // Get the user ID
211     passwd* pwd = getpwnam(userName.c_str());
212     if (pwd == nullptr)
213     {
214         lg2::error("Failed to get user ID for user:{USER}", "USER", userName);
215         return false;
216     }
217     // Change the ownership of the file
218     // Change ownership recursively for the user's home directory
219     std::string homeDir = std::format("/home/{}/", userName);
220     for (const auto& entry :
221          std::filesystem::recursive_directory_iterator(homeDir))
222     {
223         if (chown(entry.path().c_str(), pwd->pw_uid, pwd->pw_gid) != 0)
224         {
225             lg2::error("Ownership change error {PATH}", "PATH",
226                        entry.path().string());
227             return false;
228         }
229     }
230     return true;
231 }
checkMfaStatus() const232 bool Users::checkMfaStatus() const
233 {
234     return (manager.enabled() != MultiFactorAuthType::None &&
235             Interfaces::bypassedProtocol() == MultiFactorAuthType::None);
236 }
createSecretKey()237 std::string Users::createSecretKey()
238 {
239     if (!std::filesystem::exists(authAppPath))
240     {
241         lg2::error("No authenticator app found at {PATH}", "PATH", authAppPath);
242         throw UnsupportedRequest();
243     }
244     std::string path = std::format(secretKeyTempPath, userName);
245     if (!std::filesystem::exists(std::filesystem::path(path).parent_path()))
246     {
247         std::filesystem::create_directories(
248             std::filesystem::path(path).parent_path());
249     }
250     /*
251     -u no-rate-limit
252     -W minimal-window
253     -Q qr-mode (NONE, ANSI, UTF8)
254     -t time-based
255     -f force file
256     -d disallow-reuse
257     -C no-confirm no confirmation required for code provisioned
258     */
259     executeCmd(authAppPath, "-s", path.c_str(), "-u", "-W", "-Q", "NONE", "-t",
260                "-f", "-d", "-C");
261     if (!std::filesystem::exists(path))
262     {
263         lg2::error("Failed to create secret key for user {USER}", "USER",
264                    userName);
265         throw UnsupportedRequest();
266     }
267     std::ifstream file(path);
268     if (!file.is_open())
269     {
270         lg2::error("Failed to open secret key file {PATH}", "PATH", path);
271         throw UnsupportedRequest();
272     }
273     std::string secret;
274     std::getline(file, secret);
275     file.close();
276     if (!changeFileOwnership(userName))
277     {
278         throw UnsupportedRequest();
279     }
280     return secret;
281 }
verifyOTP(std::string otp)282 bool Users::verifyOTP(std::string otp)
283 {
284     if (Totp::verify(getUserName(), otp) == PAM_SUCCESS)
285     {
286         // If MFA is enabled for the user register the secret key
287         if (checkMfaStatus())
288         {
289             try
290             {
291                 std::filesystem::rename(
292                     std::format(secretKeyTempPath, getUserName()),
293                     std::format(secretKeyPath, getUserName()));
294             }
295             catch (const std::filesystem::filesystem_error& e)
296             {
297                 lg2::error("Failed to rename file: {CODE}", "CODE", e);
298                 return false;
299             }
300         }
301         else
302         {
303             std::filesystem::remove(
304                 std::format(secretKeyTempPath, getUserName()));
305         }
306         return true;
307     }
308     return false;
309 }
clearSecretFile(const std::string & path)310 static void clearSecretFile(const std::string& path)
311 {
312     if (std::filesystem::exists(path))
313     {
314         std::filesystem::remove(path);
315     }
316 }
clearGoogleAuthenticator(Users & thisp)317 static void clearGoogleAuthenticator(Users& thisp)
318 {
319     clearSecretFile(std::format(secretKeyPath, thisp.getUserName()));
320     clearSecretFile(std::format(secretKeyTempPath, thisp.getUserName()));
321 }
322 static std::map<MultiFactorAuthType, std::function<void(Users&)>>
323     mfaBypassHandlers{{MultiFactorAuthType::GoogleAuthenticator,
324                        clearGoogleAuthenticator},
__anon90507c8b0102(Users&) 325                       {MultiFactorAuthType::None, [](Users&) {}}};
326 
bypassedProtocol(MultiFactorAuthType value,bool skipSignal)327 MultiFactorAuthType Users::bypassedProtocol(MultiFactorAuthType value,
328                                             bool skipSignal)
329 {
330     auto iter = mfaBypassHandlers.find(value);
331     if (iter != end(mfaBypassHandlers))
332     {
333         iter->second(*this);
334     }
335     std::string path = std::format("{}/bypassedprotocol", getUserName());
336     manager.getSerializer().serialize(
337         path, MultiFactorAuthConfiguration::convertTypeToString(value));
338     manager.getSerializer().store();
339     return Interfaces::bypassedProtocol(value, skipSignal);
340 }
341 
secretKeyIsValid() const342 bool Users::secretKeyIsValid() const
343 {
344     std::string path = std::format(secretKeyPath, getUserName());
345     return std::filesystem::exists(path);
346 }
347 
googleAuthenticatorEnabled(Users & user,bool)348 inline void googleAuthenticatorEnabled(Users& user, bool /*unused*/)
349 {
350     clearGoogleAuthenticator(user);
351 }
352 static std::map<MultiFactorAuthType, std::function<void(Users&, bool)>>
353     mfaEnableHandlers{{MultiFactorAuthType::GoogleAuthenticator,
354                        googleAuthenticatorEnabled},
__anon90507c8b0202(Users&, bool) 355                       {MultiFactorAuthType::None, [](Users&, bool) {}}};
356 
enableMultiFactorAuth(MultiFactorAuthType type,bool value)357 void Users::enableMultiFactorAuth(MultiFactorAuthType type, bool value)
358 {
359     auto iter = mfaEnableHandlers.find(type);
360     if (iter != end(mfaEnableHandlers))
361     {
362         iter->second(*this, value);
363     }
364 }
secretKeyGenerationRequired() const365 bool Users::secretKeyGenerationRequired() const
366 {
367     return checkMfaStatus() && !secretKeyIsValid();
368 }
clearSecretKey()369 void Users::clearSecretKey()
370 {
371     if (!checkMfaStatus())
372     {
373         throw UnsupportedRequest();
374     }
375     clearGoogleAuthenticator(*this);
376 }
377 
load(JsonSerializer & ts)378 void Users::load(JsonSerializer& ts)
379 {
380     std::optional<std::string> protocol;
381     std::string path = std::format("{}/bypassedprotocol", userName);
382     ts.deserialize(path, protocol);
383     if (protocol)
384     {
385         MultiFactorAuthType type =
386             MultiFactorAuthConfiguration::convertTypeFromString(*protocol);
387         bypassedProtocol(type, true);
388         return;
389     }
390     bypassedProtocol(MultiFactorAuthType::None, true);
391     ts.serialize(path, MultiFactorAuthConfiguration::convertTypeToString(
392                            MultiFactorAuthType::None));
393 }
394 
395 } // namespace user
396 } // namespace phosphor
397