From 93f66a039d4e840d7597288b296f68d467039006 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 | 164 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 137 insertions(+), 29 deletions(-) diff --git a/http/app.hpp b/http/app.hpp index 7e946dc..d63c6cb 100644 --- a/http/app.hpp +++ b/http/app.hpp @@ -51,7 +51,7 @@ class App App& operator=(const App&&) = delete; 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 9443657..048734d 100644 --- a/http/routing.hpp +++ b/http/routing.hpp @@ -1217,7 +1217,7 @@ class Router } template - void handleUpgrade(const Request& req, + void handleUpgrade(Request& req, const std::shared_ptr& asyncResp, Adaptor&& adaptor) { @@ -1285,33 +1285,141 @@ 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::forward(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, + crow::utility::urlFromPieces( + "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