blob: 9330fc2901e3f29b76fb3398e0909a2f5f5b992c [file] [log] [blame]
#include "session_cmds.hpp"
#include "endian.hpp"
#include "sessions_manager.hpp"
#include <ipmid/api.h>
#include <chrono>
#include <ipmid/sessionhelper.hpp>
#include <ipmid/utils.hpp>
#include <phosphor-logging/log.hpp>
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::exception& 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;
}
}
auto closeSessionInstance =
session::Manager::get().getSession(reqSessionId);
uint8_t closeSessionPriv = closeSessionInstance->currentPrivilege();
if (currentSessionPriv < closeSessionPriv)
{
return ipmi::ccInsufficientPrivilege;
}
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",
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::exception& 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