Add UserPasswordExpired for local users
Adds a new UserPasswordExpired property to local User.Attributes which
represents if the account's password is expired and must be changed.
The value corresponds to the `chage` command.
Note this is distinct from UserLockedForFailedAttempt which represents
a locked account due to unsuccessful authentication atttempts.
Tested: Via busctl
- Checked local and LDAP users.
- Expired password via `passwd --expire USER`.
- Aged password via `chage USER`.
- Changed password via REST API and via the `passwd USER` command.
Signed-off-by: Joseph Reynolds <joseph-reynolds@charter.net>
Change-Id: I44585559509a422bb91c83a2a853c1a033594350
diff --git a/user_mgr.cpp b/user_mgr.cpp
index 2f22323..9694fd1 100644
--- a/user_mgr.cpp
+++ b/user_mgr.cpp
@@ -18,6 +18,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <time.h>
#include <fstream>
#include <grp.h>
#include <pwd.h>
@@ -726,6 +727,50 @@
return userLockedForFailedAttempt(userName);
}
+bool UserMgr::userPasswordExpired(const std::string &userName)
+{
+ // All user management lock has to be based on /etc/shadow
+ phosphor::user::shadow::Lock lock();
+
+ struct spwd spwd
+ {
+ };
+ struct spwd *spwdPtr = nullptr;
+ auto buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (buflen < -1)
+ {
+ // Use a default size if there is no hard limit suggested by sysconf()
+ buflen = 1024;
+ }
+ std::vector<char> buffer(buflen);
+ auto status =
+ getspnam_r(userName.c_str(), &spwd, buffer.data(), buflen, &spwdPtr);
+ // On success, getspnam_r() returns zero, and sets *spwdPtr to spwd.
+ // If no matching password record was found, these functions return 0
+ // and store NULL in *spwdPtr
+ if ((status == 0) && (&spwd == spwdPtr))
+ {
+ // Determine password validity per "chage" docs, where:
+ // spwd.sp_lstchg == 0 means password is expired, and
+ // spwd.sp_max == -1 means the password does not expire.
+ constexpr long seconds_per_day = 60 * 60 * 24;
+ long today = static_cast<long>(time(NULL)) / seconds_per_day;
+ if ((spwd.sp_lstchg == 0) ||
+ ((spwd.sp_max != -1) && ((spwd.sp_max + spwd.sp_lstchg) < today)))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ log<level::ERR>("User does not exist",
+ entry("USER_NAME=%s", userName.c_str()));
+ elog<UserNameDoesNotExist>();
+ }
+
+ return false;
+}
+
UserSSHLists UserMgr::getUserAndSshGrpList()
{
// All user management lock has to be based on /etc/shadow
@@ -952,6 +997,8 @@
userInfo.emplace("UserEnabled", user.get()->userEnabled());
userInfo.emplace("UserLockedForFailedAttempt",
user.get()->userLockedForFailedAttempt());
+ userInfo.emplace("UserPasswordExpired",
+ user.get()->userPasswordExpired());
userInfo.emplace("RemoteUser", false);
}
else