#include "session_cmds.hpp" #include "endian.hpp" #include "sessions_manager.hpp" #include <ipmid/api.h> #include <ipmid/sessionhelper.hpp> #include <ipmid/utils.hpp> #include <phosphor-logging/log.hpp> #include <chrono> using namespace std::chrono_literals; namespace command { using namespace phosphor::logging; std::vector<uint8_t> setSessionPrivilegeLevel(const std::vector<uint8_t>& inPayload, std::shared_ptr<message::Handler>& handler) { auto request = reinterpret_cast<const SetSessionPrivLevelReq*>(inPayload.data()); if (inPayload.size() != sizeof(*request)) { std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID}; return errorPayload; } if (request->reserved != 0) { std::vector<uint8_t> errorPayload{IPMI_CC_INVALID_FIELD_REQUEST}; return errorPayload; } std::vector<uint8_t> outPayload(sizeof(SetSessionPrivLevelResp)); auto response = reinterpret_cast<SetSessionPrivLevelResp*>(outPayload.data()); response->completionCode = IPMI_CC_OK; uint8_t reqPrivilegeLevel = request->reqPrivLevel; auto session = session::Manager::get().getSession(handler->sessionID); if (reqPrivilegeLevel == 0) // Just return present privilege level { response->newPrivLevel = session->currentPrivilege(); return outPayload; } if (reqPrivilegeLevel == static_cast<uint8_t>(session::Privilege::CALLBACK) || reqPrivilegeLevel > static_cast<uint8_t>(session::Privilege::OEM)) { response->completionCode = IPMI_CC_INVALID_FIELD_REQUEST; return outPayload; } if (reqPrivilegeLevel > (static_cast<uint8_t>(session->reqMaxPrivLevel) & session::reqMaxPrivMask)) { // Requested level exceeds Channel and/or User Privilege Limit response->completionCode = IPMI_CC_EXCEEDS_USER_PRIV; return outPayload; } // Use the minimum privilege of user or channel uint8_t minPriv = 0; if (session->sessionChannelAccess.privLimit < session->sessionUserPrivAccess.privilege) { minPriv = session->sessionChannelAccess.privLimit; } else { minPriv = session->sessionUserPrivAccess.privilege; } if (reqPrivilegeLevel > minPriv) { // Requested level exceeds Channel and/or User Privilege Limit response->completionCode = IPMI_CC_EXCEEDS_USER_PRIV; } else { // update current privilege of the session. session->currentPrivilege(static_cast<uint8_t>(reqPrivilegeLevel)); response->newPrivLevel = reqPrivilegeLevel; } return outPayload; } /** * @brief set the session state as teardown * * This function is to set the session state to tear down in progress if the * state is active. * * @param[in] busp - Dbus obj * @param[in] service - service name * @param[in] obj - object path * * @return success completion code if it sets the session state to * tearDownInProgress else return the corresponding error completion code. **/ uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp, const std::string& service, const std::string& obj) { try { uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty( *busp, service, obj, session::sessionIntf, "State")); if (sessionState == static_cast<uint8_t>(session::State::active)) { ipmi::setDbusProperty( *busp, service, obj, session::sessionIntf, "State", static_cast<uint8_t>(session::State::tearDownInProgress)); return ipmi::ccSuccess; } } catch (const std::exception& e) { log<level::ERR>("Failed in getting session state property", entry("service=%s", service.c_str()), entry("object path=%s", obj.c_str()), entry("interface=%s", session::sessionIntf)); return ipmi::ccUnspecifiedError; } return ipmi::ccInvalidFieldRequest; } uint8_t closeOtherNetInstanceSession(const uint32_t reqSessionId, const uint8_t reqSessionHandle, const uint8_t currentSessionPriv) { auto busp = getSdBus(); try { ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects( *busp, session::sessionManagerRootPath, session::sessionIntf); for (auto& objectTreeItr : objectTree) { const std::string obj = objectTreeItr.first; if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle)) { auto& serviceMap = objectTreeItr.second; if (serviceMap.size() != 1) { return ipmi::ccUnspecifiedError; } auto itr = serviceMap.begin(); const std::string service = itr->first; uint8_t closeSessionPriv = std::get<uint8_t>(ipmi::getDbusProperty( *busp, service, obj, session::sessionIntf, "CurrentPrivilege")); if (currentSessionPriv < closeSessionPriv) { return ipmi::ccInsufficientPrivilege; } return setSessionState(busp, service, obj); } } } catch (const sdbusplus::exception_t& e) { log<level::ERR>("Failed to fetch object from dbus", entry("INTERFACE=%s", session::sessionIntf), entry("ERRMSG=%s", e.what())); return ipmi::ccUnspecifiedError; } return ipmi::ccInvalidFieldRequest; } uint8_t closeMyNetInstanceSession(uint32_t reqSessionId, uint8_t reqSessionHandle, const uint8_t currentSessionPriv) { bool status = false; try { if (reqSessionId == session::sessionZero) { reqSessionId = session::Manager::get().getSessionIDbyHandle( reqSessionHandle & session::multiIntfaceSessionHandleMask); if (!reqSessionId) { return session::ccInvalidSessionHandle; } } } catch (const std::exception& e) { log<level::ERR>("Failed to get session manager instance or sessionID " "by sessionHandle", entry("ERRMSG=%s", e.what())); return session::ccInvalidSessionHandle; } try { auto closeSessionInstance = session::Manager::get().getSession(reqSessionId); uint8_t closeSessionPriv = closeSessionInstance->currentPrivilege(); if (currentSessionPriv < closeSessionPriv) { return ipmi::ccInsufficientPrivilege; } } catch (const std::exception& e) { log<level::ERR>("Failed to get session manager instance or sessionID", entry("ERRMSG=%s", e.what())); return session::ccInvalidSessionId; } try { status = session::Manager::get().stopSession(reqSessionId); if (!status) { return session::ccInvalidSessionId; } } catch (const std::exception& e) { log<level::ERR>( "Failed to get session manager instance or stop session", entry("ERRMSG=%s", e.what())); return ipmi::ccUnspecifiedError; } return ipmi::ccSuccess; } std::vector<uint8_t> closeSession(const std::vector<uint8_t>& inPayload, std::shared_ptr<message::Handler>& handler) { // minimum inPayload size is reqSessionId (uint32_t) // maximum inPayload size is struct CloseSessionRequest if (inPayload.size() != sizeof(uint32_t) && inPayload.size() != sizeof(CloseSessionRequest)) { std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID}; return errorPayload; } auto request = reinterpret_cast<const CloseSessionRequest*>(inPayload.data()); std::vector<uint8_t> outPayload(sizeof(CloseSessionResponse)); auto response = reinterpret_cast<CloseSessionResponse*>(outPayload.data()); uint32_t reqSessionId = request->sessionID; uint8_t ipmiNetworkInstance = 0; uint8_t currentSessionPriv = 0; uint8_t reqSessionHandle = session::invalidSessionHandle; if (inPayload.size() == sizeof(CloseSessionRequest)) { reqSessionHandle = request->sessionHandle; } if (reqSessionId == session::sessionZero && reqSessionHandle == session::invalidSessionHandle) { response->completionCode = session::ccInvalidSessionHandle; return outPayload; } if (inPayload.size() == sizeof(reqSessionId) && reqSessionId == session::sessionZero) { response->completionCode = session::ccInvalidSessionId; return outPayload; } if (reqSessionId != session::sessionZero && inPayload.size() != sizeof(reqSessionId)) { response->completionCode = ipmi::ccInvalidFieldRequest; return outPayload; } try { ipmiNetworkInstance = session::Manager::get().getNetworkInstance(); auto currentSession = session::Manager::get().getSession(handler->sessionID); currentSessionPriv = currentSession->currentPrivilege(); } catch (const sdbusplus::exception_t& e) { log<level::ERR>("Failed to fetch object from dbus", entry("INTERFACE=%s", session::sessionIntf), entry("ERRMSG=%s", e.what())); response->completionCode = ipmi::ccUnspecifiedError; return outPayload; } if (reqSessionId >> myNetInstanceSessionIdShiftMask == ipmiNetworkInstance || (reqSessionId == session::sessionZero && (reqSessionHandle >> myNetInstanceSessionHandleShiftMask == ipmiNetworkInstance))) { response->completionCode = closeMyNetInstanceSession( reqSessionId, reqSessionHandle, currentSessionPriv); session::Manager::get().scheduleSessionCleaner(100us); } else { response->completionCode = closeOtherNetInstanceSession( reqSessionId, reqSessionHandle, currentSessionPriv); } return outPayload; } } // namespace command