xref: /openbmc/phosphor-user-manager/users.cpp (revision a1a754c22305421e859b4ffeeee49909cf11a2fa)
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 
80     this->emit_object_added();
81 }
82 
83 /** @brief delete user method.
84  *  This method deletes the user as requested
85  *
86  */
delete_(void)87 void Users::delete_(void)
88 {
89     manager.deleteUser(userName);
90 }
91 
92 /** @brief update user privilege
93  *
94  *  @param[in] value - User privilege
95  */
userPrivilege(std::string value)96 std::string Users::userPrivilege(std::string value)
97 {
98     if (value == UsersIface::userPrivilege())
99     {
100         return value;
101     }
102     manager.updateGroupsAndPriv(userName, UsersIface::userGroups(), value);
103     return UsersIface::userPrivilege(value);
104 }
105 
setUserPrivilege(const std::string & value)106 void Users::setUserPrivilege(const std::string& value)
107 {
108     UsersIface::userPrivilege(value);
109 }
110 
setUserGroups(const std::vector<std::string> & groups)111 void Users::setUserGroups(const std::vector<std::string>& groups)
112 {
113     UsersIface::userGroups(groups);
114 }
115 
116 /** @brief list user privilege
117  *
118  */
userPrivilege(void) const119 std::string Users::userPrivilege(void) const
120 {
121     return UsersIface::userPrivilege();
122 }
123 
124 /** @brief update user groups
125  *
126  *  @param[in] value - User groups
127  */
userGroups(std::vector<std::string> value)128 std::vector<std::string> Users::userGroups(std::vector<std::string> value)
129 {
130     if (value == UsersIface::userGroups())
131     {
132         return value;
133     }
134     std::sort(value.begin(), value.end());
135     manager.updateGroupsAndPriv(userName, value, UsersIface::userPrivilege());
136     return UsersIface::userGroups(value);
137 }
138 
139 /** @brief list user groups
140  *
141  */
userGroups(void) const142 std::vector<std::string> Users::userGroups(void) const
143 {
144     return UsersIface::userGroups();
145 }
146 
147 /** @brief lists user enabled state
148  *
149  */
userEnabled(void) const150 bool Users::userEnabled(void) const
151 {
152     return manager.isUserEnabled(userName);
153 }
154 
setUserEnabled(bool value)155 void Users::setUserEnabled(bool value)
156 {
157     UsersIface::userEnabled(value);
158 }
159 
160 /** @brief update user enabled state
161  *
162  *  @param[in] value - bool value
163  */
userEnabled(bool value)164 bool Users::userEnabled(bool value)
165 {
166     if (value == UsersIface::userEnabled())
167     {
168         return value;
169     }
170     manager.userEnable(userName, value);
171     return UsersIface::userEnabled(value);
172 }
173 
174 /** @brief lists user locked state for failed attempt
175  *
176  **/
userLockedForFailedAttempt(void) const177 bool Users::userLockedForFailedAttempt(void) const
178 {
179     return manager.userLockedForFailedAttempt(userName);
180 }
181 
182 /** @brief unlock user locked state for failed attempt
183  *
184  * @param[in]: value - false - unlock user account, true - no action taken
185  **/
userLockedForFailedAttempt(bool value)186 bool Users::userLockedForFailedAttempt(bool value)
187 {
188     if (value != false)
189     {
190         return userLockedForFailedAttempt();
191     }
192     else
193     {
194         return manager.userLockedForFailedAttempt(userName, value);
195     }
196 }
197 
198 /** @brief indicates if the user's password is expired
199  *
200  **/
userPasswordExpired(void) const201 bool Users::userPasswordExpired(void) const
202 {
203     return manager.userPasswordExpired(userName);
204 }
changeFileOwnership(const std::string & filePath,const std::string & userName)205 bool changeFileOwnership(const std::string& filePath,
206                          const std::string& userName)
207 {
208     // Get the user ID
209     passwd* pwd = getpwnam(userName.c_str());
210     if (pwd == nullptr)
211     {
212         lg2::error("Failed to get user ID for user:{USER}", "USER", userName);
213         return false;
214     }
215     // Change the ownership of the file
216     if (chown(filePath.c_str(), pwd->pw_uid, pwd->pw_gid) != 0)
217     {
218         lg2::error("Ownership change error {PATH}", "PATH", filePath);
219         return false;
220     }
221     return true;
222 }
checkMfaStatus() const223 bool Users::checkMfaStatus() const
224 {
225     return (manager.enabled() != MultiFactorAuthType::None &&
226             Interfaces::bypassedProtocol() == MultiFactorAuthType::None);
227 }
createSecretKey()228 std::string Users::createSecretKey()
229 {
230     if (!std::filesystem::exists(authAppPath))
231     {
232         lg2::error("No authenticator app found at {PATH}", "PATH", authAppPath);
233         throw UnsupportedRequest();
234     }
235     std::string path = std::format(secretKeyTempPath, userName);
236     if (!std::filesystem::exists(std::filesystem::path(path).parent_path()))
237     {
238         std::filesystem::create_directories(
239             std::filesystem::path(path).parent_path());
240     }
241     /*
242     -u no-rate-limit
243     -W minimal-window
244     -Q qr-mode (NONE, ANSI, UTF8)
245     -t time-based
246     -f force file
247     -d disallow-reuse
248     -C no-confirm no confirmation required for code provisioned
249     */
250     executeCmd(authAppPath, "-s", path.c_str(), "-u", "-W", "-Q", "NONE", "-t",
251                "-f", "-d", "-C");
252     if (!std::filesystem::exists(path))
253     {
254         lg2::error("Failed to create secret key for user {USER}", "USER",
255                    userName);
256         throw UnsupportedRequest();
257     }
258     std::ifstream file(path);
259     if (!file.is_open())
260     {
261         lg2::error("Failed to open secret key file {PATH}", "PATH", path);
262         throw UnsupportedRequest();
263     }
264     std::string secret;
265     std::getline(file, secret);
266     file.close();
267     if (!changeFileOwnership(path, userName))
268     {
269         throw UnsupportedRequest();
270     }
271     return secret;
272 }
verifyOTP(std::string otp)273 bool Users::verifyOTP(std::string otp)
274 {
275     if (Totp::verify(getUserName(), otp) == PAM_SUCCESS)
276     {
277         // If MFA is enabled for the user register the secret key
278         if (checkMfaStatus())
279         {
280             try
281             {
282                 std::filesystem::rename(
283                     std::format(secretKeyTempPath, getUserName()),
284                     std::format(secretKeyPath, getUserName()));
285             }
286             catch (const std::filesystem::filesystem_error& e)
287             {
288                 lg2::error("Failed to rename file: {CODE}", "CODE", e);
289                 return false;
290             }
291         }
292         else
293         {
294             std::filesystem::remove(
295                 std::format(secretKeyTempPath, getUserName()));
296         }
297         return true;
298     }
299     return false;
300 }
clearSecretFile(const std::string & path)301 static void clearSecretFile(const std::string& path)
302 {
303     if (std::filesystem::exists(path))
304     {
305         std::filesystem::remove(path);
306     }
307 }
clearGoogleAuthenticator(Users & thisp)308 static void clearGoogleAuthenticator(Users& thisp)
309 {
310     clearSecretFile(std::format(secretKeyPath, thisp.getUserName()));
311     clearSecretFile(std::format(secretKeyTempPath, thisp.getUserName()));
312 }
313 static std::map<MultiFactorAuthType, std::function<void(Users&)>>
314     mfaBypassHandlers{{MultiFactorAuthType::GoogleAuthenticator,
315                        clearGoogleAuthenticator},
__anon90507c8b0102(Users&) 316                       {MultiFactorAuthType::None, [](Users&) {}}};
317 
bypassedProtocol(MultiFactorAuthType value,bool skipSignal)318 MultiFactorAuthType Users::bypassedProtocol(MultiFactorAuthType value,
319                                             bool skipSignal)
320 {
321     auto iter = mfaBypassHandlers.find(value);
322     if (iter != end(mfaBypassHandlers))
323     {
324         iter->second(*this);
325     }
326     return Interfaces::bypassedProtocol(value, skipSignal);
327 }
328 
secretKeyIsValid() const329 bool Users::secretKeyIsValid() const
330 {
331     std::string path = std::format(secretKeyPath, getUserName());
332     return std::filesystem::exists(path);
333 }
334 
googleAuthenticatorEnabled(Users & user,bool)335 inline void googleAuthenticatorEnabled(Users& user, bool /*unused*/)
336 {
337     clearGoogleAuthenticator(user);
338 }
339 static std::map<MultiFactorAuthType, std::function<void(Users&, bool)>>
340     mfaEnableHandlers{{MultiFactorAuthType::GoogleAuthenticator,
341                        googleAuthenticatorEnabled},
__anon90507c8b0202(Users&, bool) 342                       {MultiFactorAuthType::None, [](Users&, bool) {}}};
343 
enableMultiFactorAuth(MultiFactorAuthType type,bool value)344 void Users::enableMultiFactorAuth(MultiFactorAuthType type, bool value)
345 {
346     auto iter = mfaEnableHandlers.find(type);
347     if (iter != end(mfaEnableHandlers))
348     {
349         iter->second(*this, value);
350     }
351 }
secretKeyGenerationRequired() const352 bool Users::secretKeyGenerationRequired() const
353 {
354     return checkMfaStatus() && !secretKeyIsValid();
355 }
clearSecretKey()356 void Users::clearSecretKey()
357 {
358     if (!checkMfaStatus())
359     {
360         throw UnsupportedRequest();
361     }
362     clearGoogleAuthenticator(*this);
363 }
364 
365 } // namespace user
366 } // namespace phosphor
367