summaryrefslogtreecommitdiff
path: root/include/sessions.hpp
diff options
context:
space:
mode:
authorJoseph Reynolds <joseph-reynolds@charter.net>2020-02-06 23:44:32 +0300
committerJoseph Reynolds <joseph-reynolds@charter.net>2020-05-20 00:03:48 +0300
commit3bf4e63296f0b69201904b03b2470543a7e0c627 (patch)
tree7c91ca612d1af7f1afeba6cd47419fec41f85ffd /include/sessions.hpp
parent38268fa8af4964312062bd92156ab87637f82e71 (diff)
downloadbmcweb-3bf4e63296f0b69201904b03b2470543a7e0c627.tar.xz
Implement Redfish PasswordChangeRequired
This implements the Redfish PasswordChangeRequired handling. See section 13.3.7.1 "Password change required handling" in the 1.9.1 spec: https://www.dmtf.org/sites/default/files/standards/documents/DSP0266_1.9.1.pdf These portions of the spec are implemented: - Authenticatation with a correct but expired password creates a session: - The session is restricted to the ConfigureSelf privilege which allows a user to change their own password (via GET and PATCH Password for their own account). Support for the ConfigureSelf privilege is already in BMCWeb. - The session object has the PasswordChangeRequired message. - All other operations respond with http status code 403 Forbidden and include the PasswordChangeRequired message. - The ManagerAccount (URI /redfish/v1/AccountService/Accounts/USER) PasswordChangeRequired property is implemented for local accounts but not present for remote accounts. This has the following additional behavior: The PasswordChangeRequired property is updated at the start of each new REST operation, even within an existing session. This behavior implements a "dynamic" PasswordChangeRequired handling that responds to changes to the underlying "password expired" status. Specifically: - Sessions restricted by the PasswordChangeRequired handling lose that restriction when the underlying account password is changed. - Sessions become subject to the PasswordChangeRequired handling restrictions whenever the underlying account password expires. - The mechanism is to check if the password is expired at the start of every new REST API operation, effectively updating the ManagerAccount PasswordChangeRequired property each time. This makes BMCWeb responsive to changes in the underlying account due to other activity on the BMC. Notes: 1. Note that when an account password status is changed (for example, the password becomes expired or is changed) and that account has active sessions, those sessions remain. They are not deleted. Any current operations are allowed to complete. Subsequent operations with that session pick up the new password status. 2. This does not implement OWASP recommendations which call for sessions to be dropped when there is a significant change to the underlying account. For example, when the password is changed, the password becomes expired, or when the account's Role changes. OWASP's recommendation is due to the session fixation vulnerability. See the OWASP Session Management Cheat Sheet section "Renew the Session ID After Any Privilege Level Change": https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html#renew-the-session-id-after-any-privilege-level-change BMCWeb protects against session fixation vulnerabilities because it always regenerates new session IDs when successful authentication creates a new session. 3. Users authenticating via mTLS are not subject to the PasswordChangeRequired behavior because mTLS takes precedence over password-based authentication. Tested: 0. Setup: - The `passwd --expire USERNAME` command was used to expire passwords. The `chage USER` command was also used. - The following were used to change the password: Redfish API, passwd command, and the SSH password change dialog. - Tested the following via Basic Auth, /login, and Redfish login (except where Basic Auth does not create a persistent session). - Only local user account were tested. - Did not test authentication via mTLS or with LDAP users. 1. When the password is not expired, authentication behaves as usual for both correct and incorrect passwords. 2. When the password is incorrect and expired, authentication fails as usual. 3. When the password is correct but expired: A. A session is created and has the PasswordChangeRequired message. B. That session cannot access resources that require Login privilege and the 403 message contains the PasswordChangeRequired message. C. That session can be used to GET the user's account, PATCH the Password, and DELETE the session object. D. The account PasswordChangeRequired reports true. 4. While a session is established, try expiring and changing (unexpiring) the password using various mechanisms. Ensure both the session object and the ManagerAccount PasswordChangeRequired property report the correct condition, and ensure PasswordChangeRequired handling (restricting operations to ConfigureSelf when PasswordChangeRequired is true) is applied correctly. Signed-off-by: Joseph Reynolds <joseph-reynolds@charter.net> Change-Id: Iedc61dea8f949e4b182e14dc189de02d1f74d3e8
Diffstat (limited to 'include/sessions.hpp')
-rw-r--r--include/sessions.hpp19
1 files changed, 15 insertions, 4 deletions
diff --git a/include/sessions.hpp b/include/sessions.hpp
index 9d24327eab..a7ffe28921 100644
--- a/include/sessions.hpp
+++ b/include/sessions.hpp
@@ -45,6 +45,15 @@ struct UserSession
std::chrono::time_point<std::chrono::steady_clock> lastUpdated;
PersistenceType persistence;
bool cookieAuth = false;
+ bool isConfigureSelfOnly = false;
+
+ // There are two sources of truth for isConfigureSelfOnly:
+ // 1. When pamAuthenticateUser() returns PAM_NEW_AUTHTOK_REQD.
+ // 2. D-Bus User.Manager.GetUserInfo property UserPasswordExpired.
+ // These should be in sync, but the underlying condition can change at any
+ // time. For example, a password can expire or be changed outside of
+ // bmcweb. The value stored here is updated at the start of each
+ // operation and used as the truth within bmcweb.
/**
* @brief Fills object with data from UserSession's JSON representation
@@ -196,7 +205,8 @@ class SessionStore
public:
std::shared_ptr<UserSession> generateUserSession(
const std::string_view username,
- PersistenceType persistence = PersistenceType::TIMEOUT)
+ PersistenceType persistence = PersistenceType::TIMEOUT,
+ bool isConfigureSelfOnly = false)
{
// TODO(ed) find a secure way to not generate session identifiers if
// persistence is set to SINGLE_REQUEST
@@ -244,9 +254,10 @@ class SessionStore
}
}
- auto session = std::make_shared<UserSession>(UserSession{
- uniqueId, sessionToken, std::string(username), csrfToken,
- std::chrono::steady_clock::now(), persistence});
+ auto session = std::make_shared<UserSession>(
+ UserSession{uniqueId, sessionToken, std::string(username),
+ csrfToken, std::chrono::steady_clock::now(),
+ persistence, false, isConfigureSelfOnly});
auto it = authTokens.emplace(std::make_pair(sessionToken, session));
// Only need to write to disk if session isn't about to be destroyed.
needWrite = persistence == PersistenceType::TIMEOUT;