summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNinad Palsule <ninadpalsule@us.ibm.com>2023-03-28 01:19:55 +0300
committerEd Tanous <ed@tanous.net>2023-05-20 01:41:14 +0300
commit3e72c2027aa4e64b9892ab0d3970358ba446f1fa (patch)
tree3f35b4bbe4d07b4e6df6ada02df888e22b4a792d
parent15b89725c5dfab240685a4095015ca8479dc44d6 (diff)
downloadbmcweb-3e72c2027aa4e64b9892ab0d3970358ba446f1fa.tar.xz
Added new pre-defined usergroup called hostconsole
The new pre-defined usergroup named "hostconsole" is added to differentiate access between host console and manager console. The only users allowed to interact with host console are part of the "hostconsole" group and they are in an administrator role. Note: The changes are spread across multiple repositories listed under "Related commits:" The bmcweb changes to incorporate new group are as follows: - The new user is added in the hostconsole group only if it has an administrative role. - The ssh usergroup is only translated to ManagerConsole redfish group and hostconsole usergroup is translated to HostConsole redfish group. - The following changes are made to check the privileges for host console access - The new OEM privilege "OpenBMCHostConsole" added for host console access. This privilege is not shared externally hence it is not documented. - Updated obmc_console BMCWEB_ROUTE to use the new privilege. - Router functions now save user role and user groups in the session - getUserPrivileges() function now takes session reference instead of user role. This function now also checks for the user group "hostconsole" and add the new privilege if user is member of this group. - Updated all callers of the getUserPrivileges to pass session reference. - Added test to validate that new privilege is set correctly. Tested: Loaded code on the system and validated that; - New user gets added in hostconsole group. NOTE: Prior to this commit all groups are assigned to new user. This drop does not change that behavior. - Access from the web gui is only available for users in hostconsole group. Used IBM internal simulator called simics to test this. This simulator allows accessing openbmc from GUI. - Checked the role collection and there is no change. $ curl -k -H "X-Auth-Token: $TOKEN" -X GET \ https://${bmc}/redfish/v1/AccountService/Roles $ curl -k -H "X-Auth-Token: $TOKEN" -X GET \ https://${bmc}/redfish/v1/AccountService/Roles/Administrator $ curl -k -H "X-Auth-Token: $TOKEN" -X GET \ https://${bmc}/redfish/v1/AccountService/Roles/ReadOnly $ curl -k -H "X-Auth-Token: $TOKEN" -X GET \ https://${bmc}/redfish/v1/AccountService/Roles/Operator - HostConsole is in AccountType when hostconsole group is present in UserGroups D-Bus property $ id user99 uid=1006(user99) gid=100(users) groups=1000(priv-admin),1005(web),\ 1006(redfish),1013(hostconsole),100(users) $ curl -k https://${bmc}/redfish/v1/AccountService/Accounts/user99 { "@odata.id": "/redfish/v1/AccountService/Accounts/user99", "@odata.type": "#ManagerAccount.v1_4_0.ManagerAccount", "AccountTypes": [ "HostConsole", "Redfish", "WebUI", "ManagerConsole" ], "Description": "User Account", "Enabled": true, "Id": "user99", "Links": { "Role": { "@odata.id": "/redfish/v1/AccountService/Roles/Administrator" } }, "Locked": false, "Locked@Redfish.AllowableValues": [ "false" ], "Name": "User Account", "Password": null, "PasswordChangeRequired": false, "RoleId": "Administrator", "UserName": "user99" - The hostconsole group is not present for readonly or operator users and also made sure that console access is not provided. This testing is done one the system and console access was tried by modifying the https://github.com/openbmc/bmcweb/blob/master/scripts/websocket_test.py + curl -k https://${bmc}/redfish/v1/AccountService/Accounts/user99 { "@odata.id": "/redfish/v1/AccountService/Accounts/user99", "@odata.type": "#ManagerAccount.v1_4_0.ManagerAccount", "AccountTypes": [ "IPMI", "Redfish", "WebUI", "ManagerConsole" ], "Description": "User Account", "Enabled": true, "Id": "user99", "Links": { "Role": { "@odata.id": "/redfish/v1/AccountService/Roles/ReadOnly" } }, "Locked": false, "Locked@Redfish.AllowableValues": [ "false" ], "Name": "User Account", "Password": null, "PasswordChangeRequired": false, "RoleId": "ReadOnly", "UserName": "user99" [INFO "http_connection.hpp":209] Request: 0x150ac38 HTTP/1.1 GET /console0 ::ffff:x.x.xx.xxx [DEBUG "routing.hpp":1265] Matched rule (upgrade) '/console0' 1 / 2 [DEBUG "routing.hpp":1084] userName = user99 userRole = priv-user [DEBUG "routing.hpp":1123] IsUserPrivileged: group=ipmi [DEBUG "routing.hpp":1123] IsUserPrivileged: group=redfish [DEBUG "routing.hpp":1123] IsUserPrivileged: group=ssh [DEBUG "routing.hpp":1123] IsUserPrivileged: group=web [DEBUG "routing.hpp":93] checkPrivileges: BASE USER: Login [DEBUG "routing.hpp":93] checkPrivileges: BASE USER: ConfigureSelf [DEBUG "routing.hpp":113] checkPrivileges: OEM REQUIRED: OpenBMCHostConsole [ERROR "routing.hpp":1192] Insufficient Privilege + curl -k https://${bmc}/redfish/v1/AccountService/Accounts/user99 { "@odata.id": "/redfish/v1/AccountService/Accounts/user99", "@odata.type": "#ManagerAccount.v1_4_0.ManagerAccount", "AccountTypes": [ "IPMI", "Redfish", "WebUI", "ManagerConsole" ], "Description": "User Account", "Enabled": true, "Id": "user99", "Links": { "Role": { "@odata.id": "/redfish/v1/AccountService/Roles/Operator" } }, "Locked": false, "Locked@Redfish.AllowableValues": [ "false" ], "Name": "User Account", "Password": null, "PasswordChangeRequired": false, "RoleId": "Operator", "UserName": "user99" [INFO "http_connection.hpp":209] Request: 0x21c7c38 HTTP/1.1 GET /console0 ::ffff:x.x.xx.xxx [DEBUG "routing.hpp":1265] Matched rule (upgrade) '/console0' 1 / 2 [DEBUG "routing.hpp":1084] userName = user99 userRole = priv-operator [DEBUG "routing.hpp":1123] IsUserPrivileged: group=ipmi [DEBUG "routing.hpp":1123] IsUserPrivileged: group=redfish [DEBUG "routing.hpp":1123] IsUserPrivileged: group=ssh [DEBUG "routing.hpp":1123] IsUserPrivileged: group=web [DEBUG "routing.hpp":93] checkPrivileges: BASE USER: Login [DEBUG "routing.hpp":93] checkPrivileges: BASE USER: ConfigureComponents [DEBUG "routing.hpp":93] checkPrivileges: BASE USER: ConfigureSelf [DEBUG "routing.hpp":113] checkPrivileges: OEM REQUIRED: OpenBMCHostConsole [ERROR "routing.hpp":1192] Insufficient Privilege Related commits: NOTE: docs, openbmc, obmc-console changes are already merged. bmcweb and phosphor-user-manager will be merged together. docs: https://gerrit.openbmc.org/c/openbmc/docs/+/60968 phosphor-user-manager: https://gerrit.openbmc.org/c/openbmc/phosphor-user-manager/+/61583 openbmc: https://gerrit.openbmc.org/c/openbmc/openbmc/+/61582 obmc-console: https://gerrit.openbmc.org/c/openbmc/obmc-console/+/61581 bmcweb: https://gerrit.openbmc.org/c/openbmc/bmcweb/+/61580 Change-Id: Ia5a33dafc9a76444e6a8e74e752f0f90cb0a31c8 Signed-off-by: Ninad Palsule <ninadpalsule@us.ibm.com>
-rw-r--r--http/routing.hpp53
-rw-r--r--include/obmc_console.hpp2
-rw-r--r--include/sessions.hpp2
-rw-r--r--redfish-core/include/privileges.hpp58
-rw-r--r--redfish-core/lib/account_service.hpp45
-rw-r--r--redfish-core/lib/certificate_service.hpp8
-rw-r--r--redfish-core/lib/network_protocol.hpp8
-rw-r--r--redfish-core/lib/redfish_sessions.hpp2
-rw-r--r--test/redfish-core/include/privileges_test.cpp9
9 files changed, 145 insertions, 42 deletions
diff --git a/http/routing.hpp b/http/routing.hpp
index 84213b2289..eb87e72b5d 100644
--- a/http/routing.hpp
+++ b/http/routing.hpp
@@ -1029,22 +1029,25 @@ class Router
return findRoute;
}
- static bool isUserPrivileged(
- Request& req, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- BaseRule& rule, const dbus::utility::DBusPropertiesMap& userInfoMap)
+ // Populate session with user information.
+ static bool
+ populateUserInfo(Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const dbus::utility::DBusPropertiesMap& userInfoMap)
{
- std::string userRole{};
const std::string* userRolePtr = nullptr;
const bool* remoteUser = nullptr;
const bool* passwordExpired = nullptr;
+ const std::vector<std::string>* userGroups = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
redfish::dbus_utils::UnpackErrorPrinter(), userInfoMap,
"UserPrivilege", userRolePtr, "RemoteUser", remoteUser,
- "UserPasswordExpired", passwordExpired);
+ "UserPasswordExpired", passwordExpired, "UserGroups", userGroups);
if (!success)
{
+ BMCWEB_LOG_ERROR << "Failed to unpack user properties.";
asyncResp->res.result(
boost::beast::http::status::internal_server_error);
return false;
@@ -1052,7 +1055,7 @@ class Router
if (userRolePtr != nullptr)
{
- userRole = *userRolePtr;
+ req.session->userRole = *userRolePtr;
BMCWEB_LOG_DEBUG << "userName = " << req.session->username
<< " userRole = " << *userRolePtr;
}
@@ -1082,15 +1085,32 @@ class Router
expired = *passwordExpired;
}
- // Get the user's privileges from the role
- redfish::Privileges userPrivileges =
- redfish::getUserPrivileges(userRole);
-
// Set isConfigureSelfOnly based on D-Bus results. This
// ignores the results from both pamAuthenticateUser and the
// value from any previous use of this session.
req.session->isConfigureSelfOnly = expired;
+ if (userGroups != nullptr)
+ {
+ // Populate session with user groups.
+ for (const auto& userGroup : *userGroups)
+ {
+ req.session->userGroups.emplace_back(userGroup);
+ }
+ }
+
+ return true;
+ }
+
+ static bool
+ isUserPrivileged(Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ BaseRule& rule)
+ {
+ // Get the user's privileges from the role
+ redfish::Privileges userPrivileges =
+ redfish::getUserPrivileges(*req.session);
+
// Modify privileges if isConfigureSelfOnly.
if (req.session->isConfigureSelfOnly)
{
@@ -1114,8 +1134,7 @@ class Router
return false;
}
- req.userRole = userRole;
-
+ req.userRole = req.session->userRole;
return true;
}
@@ -1134,7 +1153,15 @@ class Router
return;
}
- if (!Router::isUserPrivileged(req, asyncResp, rule, userInfoMap))
+ if (!populateUserInfo(req, asyncResp, userInfoMap))
+ {
+ BMCWEB_LOG_ERROR << "Failed to populate user information";
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ return;
+ }
+
+ if (!Router::isUserPrivileged(req, asyncResp, rule))
{
// User is not privileged
BMCWEB_LOG_ERROR << "Insufficient Privilege";
diff --git a/include/obmc_console.hpp b/include/obmc_console.hpp
index fac489ad6d..cf90c0ed94 100644
--- a/include/obmc_console.hpp
+++ b/include/obmc_console.hpp
@@ -121,7 +121,7 @@ inline void connectHandler(const boost::system::error_code& ec)
inline void requestRoutes(App& app)
{
BMCWEB_ROUTE(app, "/console0")
- .privileges({{"ConfigureComponents", "ConfigureManager"}})
+ .privileges({{"OpenBMCHostConsole"}})
.websocket()
.onopen(
[](crow::websocket::Connection& conn) {
diff --git a/include/sessions.hpp b/include/sessions.hpp
index 6bb6a7a0e3..26b30306fa 100644
--- a/include/sessions.hpp
+++ b/include/sessions.hpp
@@ -40,6 +40,8 @@ struct UserSession
std::chrono::time_point<std::chrono::steady_clock> lastUpdated;
PersistenceType persistence{PersistenceType::TIMEOUT};
bool isConfigureSelfOnly = false;
+ std::string userRole{};
+ std::vector<std::string> userGroups{};
// There are two sources of truth for isConfigureSelfOnly:
// 1. When pamAuthenticateUser() returns PAM_NEW_AUTHTOK_REQD.
diff --git a/redfish-core/include/privileges.hpp b/redfish-core/include/privileges.hpp
index 08e554a86c..bb2c2f3024 100644
--- a/redfish-core/include/privileges.hpp
+++ b/redfish-core/include/privileges.hpp
@@ -16,6 +16,7 @@
#pragma once
#include "logging.hpp"
+#include "sessions.hpp"
#include <boost/beast/http/verb.hpp>
#include <boost/container/flat_map.hpp>
@@ -53,10 +54,15 @@ constexpr const size_t basePrivilegeCount = basePrivileges.size();
/** @brief Max number of privileges per type */
constexpr const size_t maxPrivilegeCount = 32;
-/** @brief A vector of all privilege names and their indexes */
+/**
+ * @brief A vector of all privilege names and their indexes
+ * The privilege "OpenBMCHostConsole" is added to users who are members of the
+ * "hostconsole" user group. This privilege is required to access the host
+ * console.
+ */
static const std::array<std::string, maxPrivilegeCount> privilegeNames{
- "Login", "ConfigureManager", "ConfigureComponents", "ConfigureSelf",
- "ConfigureUsers"};
+ "Login", "ConfigureManager", "ConfigureComponents",
+ "ConfigureSelf", "ConfigureUsers", "OpenBMCHostConsole"};
/**
* @brief Redfish privileges
@@ -214,30 +220,46 @@ class Privileges
std::bitset<maxPrivilegeCount> privilegeBitset = 0;
};
-inline const Privileges& getUserPrivileges(const std::string& userRole)
+inline Privileges getUserPrivileges(const persistent_data::UserSession& session)
{
- // Redfish privilege : Administrator
- if (userRole == "priv-admin")
+ // default to no access
+ Privileges privs;
+
+ // Check if user is member of hostconsole group
+ for (const auto& userGroup : session.userGroups)
{
- static Privileges admin{"Login", "ConfigureManager", "ConfigureSelf",
- "ConfigureUsers", "ConfigureComponents"};
- return admin;
+ if (userGroup == "hostconsole")
+ {
+ // Redfish privilege : host console access
+ privs.setSinglePrivilege("OpenBMCHostConsole");
+ break;
+ }
}
- if (userRole == "priv-operator")
+
+ if (session.userRole == "priv-admin")
+ {
+ // Redfish privilege : Administrator
+ privs.setSinglePrivilege("Login");
+ privs.setSinglePrivilege("ConfigureManager");
+ privs.setSinglePrivilege("ConfigureSelf");
+ privs.setSinglePrivilege("ConfigureUsers");
+ privs.setSinglePrivilege("ConfigureComponents");
+ }
+ else if (session.userRole == "priv-operator")
{
// Redfish privilege : Operator
- static Privileges op{"Login", "ConfigureSelf", "ConfigureComponents"};
- return op;
+ privs.setSinglePrivilege("Login");
+ privs.setSinglePrivilege("ConfigureSelf");
+ privs.setSinglePrivilege("ConfigureComponents");
}
- if (userRole == "priv-user")
+ else if (session.userRole == "priv-user")
{
// Redfish privilege : Readonly
- static Privileges readOnly{"Login", "ConfigureSelf"};
- return readOnly;
+ privs.setSinglePrivilege("Login");
+ privs.setSinglePrivilege("ConfigureSelf");
}
- // Redfish privilege : NoAccess
- static Privileges noaccess;
- return noaccess;
+
+ return privs;
}
/**
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
index b4c920557e..c8fd196af3 100644
--- a/redfish-core/lib/account_service.hpp
+++ b/redfish-core/lib/account_service.hpp
@@ -135,9 +135,14 @@ inline bool translateUserGroup(const std::vector<std::string>& userGroups,
}
else if (userGroup == "ssh")
{
- accountTypes.emplace_back("HostConsole");
accountTypes.emplace_back("ManagerConsole");
}
+ else if (userGroup == "hostconsole")
+ {
+ // The hostconsole group controls who can access the host console
+ // port via ssh and websocket.
+ accountTypes.emplace_back("HostConsole");
+ }
else if (userGroup == "web")
{
// 'web' is one of the valid groups in the UserGroups property of
@@ -1293,6 +1298,13 @@ inline void
{
return;
}
+
+ if (req.session == nullptr)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
asyncResp->res.addHeader(
boost::beast::http::field::link,
"</redfish/v1/JsonSchemas/AccountService/AccountService.json>; rel=describedby");
@@ -1327,7 +1339,7 @@ inline void
// ConfigureManager can access then only display when the user has
// permissions ConfigureManager
Privileges effectiveUserPrivileges =
- redfish::getUserPrivileges(req.userRole);
+ redfish::getUserPrivileges(*req.session);
if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
effectiveUserPrivileges))
@@ -1526,6 +1538,13 @@ inline void handleAccountCollectionGet(
{
return;
}
+
+ if (req.session == nullptr)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
asyncResp->res.addHeader(
boost::beast::http::field::link,
"</redfish/v1/JsonSchemas/ManagerAccountCollection.json>; rel=describedby");
@@ -1538,7 +1557,7 @@ inline void handleAccountCollectionGet(
asyncResp->res.jsonValue["Description"] = "BMC User Accounts";
Privileges effectiveUserPrivileges =
- redfish::getUserPrivileges(req.userRole);
+ redfish::getUserPrivileges(*req.session);
std::string thisUser;
if (req.session)
@@ -1646,14 +1665,26 @@ inline void processAfterGetAllGroups(
const std::vector<std::string>& allGroupsList)
{
+ std::vector<std::string> userGroups;
+ for (const auto& grp : allGroupsList)
+ {
+ // Console access is provided to the user who is a member of
+ // hostconsole group and has a administrator role. So, set
+ // hostconsole group only for the administrator.
+ if ((grp != "hostconsole") || (roleId == "priv-admin"))
+ {
+ userGroups.emplace_back(grp);
+ }
+ }
+
crow::connections::systemBus->async_method_call(
[asyncResp, username, password](const boost::system::error_code& ec2,
sdbusplus::message_t& m) {
processAfterCreateUser(asyncResp, username, password, ec2, m);
},
"xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
- "xyz.openbmc_project.User.Manager", "CreateUser", username,
- allGroupsList, *roleId, *enabled);
+ "xyz.openbmc_project.User.Manager", "CreateUser", username, userGroups,
+ *roleId, *enabled);
}
inline void handleAccountCollectionPost(
@@ -1754,7 +1785,7 @@ inline void
// have permissions to modify other users, so re-run the auth
// check with the same permissions, minus ConfigureSelf.
Privileges effectiveUserPrivileges =
- redfish::getUserPrivileges(req.userRole);
+ redfish::getUserPrivileges(*req.session);
Privileges requiredPermissionsToChangeNonSelf = {"ConfigureUsers",
"ConfigureManager"};
if (!effectiveUserPrivileges.isSupersetOf(
@@ -1965,7 +1996,7 @@ inline void
}
Privileges effectiveUserPrivileges =
- redfish::getUserPrivileges(req.userRole);
+ redfish::getUserPrivileges(*req.session);
Privileges configureUsers = {"ConfigureUsers"};
bool userHasConfigureUsers =
effectiveUserPrivileges.isSupersetOf(configureUsers);
diff --git a/redfish-core/lib/certificate_service.hpp b/redfish-core/lib/certificate_service.hpp
index 027e05ddb4..d4df6c8e01 100644
--- a/redfish-core/lib/certificate_service.hpp
+++ b/redfish-core/lib/certificate_service.hpp
@@ -404,6 +404,12 @@ inline void handleCertificateServiceGet(
return;
}
+ if (req.session == nullptr)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
asyncResp->res.jsonValue["@odata.type"] =
"#CertificateService.v1_0_0.CertificateService";
asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/CertificateService";
@@ -415,7 +421,7 @@ inline void handleCertificateServiceGet(
// only ConfigureManager can access then only display when the user
// has permissions ConfigureManager
Privileges effectiveUserPrivileges =
- redfish::getUserPrivileges(req.userRole);
+ redfish::getUserPrivileges(*req.session);
if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
effectiveUserPrivileges))
{
diff --git a/redfish-core/lib/network_protocol.hpp b/redfish-core/lib/network_protocol.hpp
index e4a4b56e8a..be842462a6 100644
--- a/redfish-core/lib/network_protocol.hpp
+++ b/redfish-core/lib/network_protocol.hpp
@@ -154,6 +154,12 @@ inline void afterNetworkPortRequest(
inline void getNetworkData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const crow::Request& req)
{
+ if (req.session == nullptr)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
asyncResp->res.addHeader(
boost::beast::http::field::link,
"</redfish/v1/JsonSchemas/ManagerNetworkProtocol/NetworkProtocol.json>; rel=describedby");
@@ -216,7 +222,7 @@ inline void getNetworkData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
});
Privileges effectiveUserPrivileges =
- redfish::getUserPrivileges(req.userRole);
+ redfish::getUserPrivileges(*req.session);
// /redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates is
// something only ConfigureManager can access then only display when
diff --git a/redfish-core/lib/redfish_sessions.hpp b/redfish-core/lib/redfish_sessions.hpp
index c41b0df9f0..06083335b0 100644
--- a/redfish-core/lib/redfish_sessions.hpp
+++ b/redfish-core/lib/redfish_sessions.hpp
@@ -112,7 +112,7 @@ inline void
session->username != req.session->username)
{
Privileges effectiveUserPrivileges =
- redfish::getUserPrivileges(req.userRole);
+ redfish::getUserPrivileges(*req.session);
if (!effectiveUserPrivileges.isSupersetOf({"ConfigureUsers"}))
{
diff --git a/test/redfish-core/include/privileges_test.cpp b/test/redfish-core/include/privileges_test.cpp
index b7f7cbcc42..59a8ccd4bd 100644
--- a/test/redfish-core/include/privileges_test.cpp
+++ b/test/redfish-core/include/privileges_test.cpp
@@ -134,5 +134,14 @@ TEST(PrivilegeTest, GetActivePrivilegeNames)
expectedPrivileges[2], expectedPrivileges[3],
expectedPrivileges[4]));
}
+
+TEST(PrivilegeTest, PrivilegeHostConsoleConstructor)
+{
+ Privileges privileges{"OpenBMCHostConsole"};
+
+ EXPECT_THAT(privileges.getActivePrivilegeNames(PrivilegeType::OEM),
+ UnorderedElementsAre("OpenBMCHostConsole"));
+}
+
} // namespace
} // namespace redfish