From aabe4718b8e6c1f7b91af29cbaf85d5fa1fa0a99 Mon Sep 17 00:00:00 2001 From: P Dheeraj Srujan Kumar Date: Mon, 18 Oct 2021 22:55:38 +0530 Subject: [PATCH] Add Support for privilege check in handleUpgrade This commit enables privilege check for user(s) in case of upgraded connections. Currently users with no privileges will also be able to access Websockets connections (Ex: KVM). Tested: - websocket_test.py Passed - Admin and Operator users were able to access KVM on WebUI - Readonly User was unable to access KVM on WebUI Change-Id: Id9d33aeca24d8fafb2e9dcc28c46a48930740cd6 Signed-off-by: P Dheeraj Srujan Kumar --- http/app.hpp | 2 +- http/routing.hpp | 162 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 136 insertions(+), 28 deletions(-) diff --git a/http/app.hpp b/http/app.hpp index c46dcf7..dd51eee 100644 --- a/http/app.hpp +++ b/http/app.hpp @@ -45,7 +45,7 @@ class App } template - void handleUpgrade(const Request& req, + void handleUpgrade(Request& req, const std::shared_ptr& asyncResp, Adaptor&& adaptor) { diff --git a/http/routing.hpp b/http/routing.hpp index acc99dc..e2a8fbb 100644 --- a/http/routing.hpp +++ b/http/routing.hpp @@ -1209,7 +1209,7 @@ class Router } template - void handleUpgrade(const Request& req, + void handleUpgrade(Request& req, const std::shared_ptr& asyncResp, Adaptor&& adaptor) { @@ -1277,32 +1277,140 @@ class Router << "' " << static_cast(req.method()) << " / " << rules[ruleIndex]->getMethods(); - // any uncaught exceptions become 500s - try - { - // Creating temporary response object to call handleUpgrade - // We cannot pass the asyncResp as it will be destroyed - // The response object is not initialized as handleUpgrade wouldn't - // be using this object - crow::Response resp; - rules[ruleIndex]->handleUpgrade(req, resp, std::move(adaptor)); - } - catch (const std::exception& e) - { - BMCWEB_LOG_ERROR << "An uncaught exception occurred: " << e.what(); - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } - catch (...) - { - BMCWEB_LOG_ERROR - << "An uncaught exception occurred. The type was unknown " - "so no information was available."; - asyncResp->res.result( - boost::beast::http::status::internal_server_error); - return; - } + crow::connections::systemBus->async_method_call( + [&req, asyncResp, &rules, ruleIndex, &adaptor]( + const boost::system::error_code ec, + std::map>> + userInfo) { + if (ec) + { + BMCWEB_LOG_ERROR << "GetUserInfo failed..."; + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + + const std::string* userRolePtr = nullptr; + auto userInfoIter = userInfo.find("UserPrivilege"); + if (userInfoIter != userInfo.end()) + { + userRolePtr = + std::get_if(&userInfoIter->second); + } + + std::string userRole{}; + if (userRolePtr != nullptr) + { + userRole = *userRolePtr; + BMCWEB_LOG_DEBUG << "userName = " << req.session->username + << " userRole = " << *userRolePtr; + } + + bool* remoteUserPtr = nullptr; + auto remoteUserIter = userInfo.find("RemoteUser"); + if (remoteUserIter != userInfo.end()) + { + remoteUserPtr = std::get_if(&remoteUserIter->second); + } + if (remoteUserPtr == nullptr) + { + BMCWEB_LOG_ERROR + << "RemoteUser property missing or wrong type"; + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + bool remoteUser = *remoteUserPtr; + + bool passwordExpired = false; // default for remote user + if (!remoteUser) + { + bool* passwordExpiredPtr = nullptr; + auto passwordExpiredIter = + userInfo.find("UserPasswordExpired"); + if (passwordExpiredIter != userInfo.end()) + { + passwordExpiredPtr = + std::get_if(&passwordExpiredIter->second); + } + if (passwordExpiredPtr != nullptr) + { + passwordExpired = *passwordExpiredPtr; + } + else + { + BMCWEB_LOG_ERROR + << "UserPasswordExpired property is expected for" + " local user but is missing or wrong type"; + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + } + + // Get the userprivileges 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 = passwordExpired; + + // Modifyprivileges if isConfigureSelfOnly. + if (req.session->isConfigureSelfOnly) + { + // Remove allprivileges except ConfigureSelf + userPrivileges = userPrivileges.intersection( + redfish::Privileges{"ConfigureSelf"}); + BMCWEB_LOG_DEBUG << "Operation limited to ConfigureSelf"; + } + + if (!rules[ruleIndex]->checkPrivileges(userPrivileges)) + { + asyncResp->res.result( + boost::beast::http::status::forbidden); + if (req.session->isConfigureSelfOnly) + { + redfish::messages::passwordChangeRequired( + asyncResp->res, + "/redfish/v1/AccountService/Accounts/" + + req.session->username); + } + return; + } + + req.userRole = userRole; + + // any uncaught exceptions become 500s + try + { + crow::Response resp; + rules[ruleIndex]->handleUpgrade(req, resp, + std::move(adaptor)); + } + catch (std::exception& e) + { + BMCWEB_LOG_ERROR << "An uncaught exception occurred: " + << e.what(); + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + catch (...) + { + BMCWEB_LOG_ERROR + << "An uncaught exception occurred. The type was " + "unknown so no information was available."; + asyncResp->res.result( + boost::beast::http::status::internal_server_error); + return; + } + }, + "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", + "xyz.openbmc_project.User.Manager", "GetUserInfo", + req.session->username); } void handle(Request& req, -- 2.17.1