#pragma once #include "auth_algo.hpp" #include "crypt_algo.hpp" #include "endian.hpp" #include "integrity_algo.hpp" #include "prng.hpp" #include "socket_channel.hpp" #include <ipmid/api.hpp> #include <ipmid/sessiondef.hpp> #include <sdbusplus/bus.hpp> #include <sdbusplus/server/object.hpp> #include <user_channel/channel_layer.hpp> #include <user_channel/user_layer.hpp> #include <xyz/openbmc_project/Ipmi/SessionInfo/server.hpp> #include <chrono> #include <exception> #include <list> #include <memory> #include <string> #include <unordered_map> #include <vector> namespace session { using namespace std::chrono_literals; using SessionID = uint32_t; enum class Privilege : uint8_t { HIGHEST_MATCHING, CALLBACK, USER, OPERATOR, ADMIN, OEM, }; // Mask to get only the privilege from requested maximum privlege (RAKP message // 1) constexpr uint8_t reqMaxPrivMask = 0xF; /** * @struct SequenceNumbers Session Sequence Numbers * * IPMI v2.0 RMCP+ Session Sequence Numbers are used for rejecting packets that * may have been duplicated by the network or intentionally replayed. There are * two sets of Session SequenceNumbers for a given session.One set of inbound * and outbound sequence numbers is used for authenticated (signed) packets, * and the other set is used for unauthenticated packets. * * The individual Session Sequence Numbers is are initialized to zero whenever * a session is created and incremented by one at the start of outbound * processing for a given packet (i.e. the first transmitted packet has a ‘1’ * as the sequence number, not 0). Session Sequence numbers are incremented for * every packet that is transmitted by a given sender, regardless of whether * the payload for the packet is a ‘retry’ or not. */ struct SequenceNumbers { auto get(bool inbound = true) const { return inbound ? in : out; } void set(uint32_t seqNumber, bool inbound = true) { inbound ? (in = seqNumber) : (out = seqNumber); } auto increment() { return ++out; } private: uint32_t in = 0; uint32_t out = 0; }; /** * @class Session * * Encapsulates the data related to an IPMI Session * * Authenticated IPMI communication to the BMC is accomplished by establishing * a session. Once established, a session is identified by a Session ID. The * Session ID may be thought of as a handle that identifies a connection between * a given remote user and the BMC. The specification supports having multiple * active sessions established with the BMC. It is recommended that a BMC * implementation support at least four simultaneous sessions */ using SessionIface = sdbusplus::server::object_t< sdbusplus::xyz::openbmc_project::Ipmi::server::SessionInfo>; class Session : public SessionIface { public: Session() = delete; ~Session() = default; Session(const Session&) = delete; Session& operator=(const Session&) = delete; Session(Session&&) = delete; Session& operator=(Session&&) = delete; /** * @brief Session Constructor * * This is issued by the Session Manager when a session is started for * the Open SessionRequest command * * @param[in] inRemoteConsoleSessID - Remote Console Session ID * @param[in] priv - Privilege Level requested in the Command */ Session(sdbusplus::bus_t& bus, const char* path, SessionID inRemoteConsoleSessID, SessionID BMCSessionID, char priv) : SessionIface(bus, path) { reqMaxPrivLevel = static_cast<session::Privilege>(priv); bmcSessionID = BMCSessionID; remoteConsoleSessionID = inRemoteConsoleSessID; } auto getBMCSessionID() const { return bmcSessionID; } auto getRCSessionID() const { return remoteConsoleSessionID; } auto getAuthAlgo() const { if (authAlgoInterface) { return authAlgoInterface.get(); } else { throw std::runtime_error("Authentication Algorithm Empty"); } } void setAuthAlgo(std::unique_ptr<cipher::rakp_auth::Interface>&& inAuthAlgo) { authAlgoInterface = std::move(inAuthAlgo); } /** * @brief Get Session's Integrity Algorithm * * @return pointer to the integrity algorithm */ auto getIntegrityAlgo() const { if (integrityAlgoInterface) { return integrityAlgoInterface.get(); } else { throw std::runtime_error("Integrity Algorithm Empty"); } } /** * @brief Set Session's Integrity Algorithm * * @param[in] integrityAlgo - unique pointer to integrity algorithm * instance */ void setIntegrityAlgo( std::unique_ptr<cipher::integrity::Interface>&& integrityAlgo) { integrityAlgoInterface = std::move(integrityAlgo); } /** @brief Check if integrity algorithm is enabled for this session. * * @return true if integrity algorithm is enabled else false. */ auto isIntegrityAlgoEnabled() { return integrityAlgoInterface ? true : false; } /** * @brief Get Session's Confidentiality Algorithm * * @return pointer to the confidentiality algorithm */ auto getCryptAlgo() const { if (cryptAlgoInterface) { return cryptAlgoInterface.get(); } else { throw std::runtime_error("Confidentiality Algorithm Empty"); } } /** * @brief Set Session's Confidentiality Algorithm * * @param[in] confAlgo - unique pointer to confidentiality algorithm * instance */ void setCryptAlgo(std::unique_ptr<cipher::crypt::Interface>&& cryptAlgo) { cryptAlgoInterface = std::move(cryptAlgo); } /** @brief Check if confidentiality algorithm is enabled for this * session. * * @return true if confidentiality algorithm is enabled else false. */ auto isCryptAlgoEnabled() { return cryptAlgoInterface ? true : false; } void updateLastTransactionTime() { lastTime = std::chrono::steady_clock::now(); } /** * @brief Session Active Status * * Session Active status is decided upon the Session State and the last * transaction time is compared against the session inactivity timeout. * * @param[in] activeGrace - microseconds of idle time for active sessions * @param[in] setupGrace - microseconds of idle time for sessions in setup * */ bool isSessionActive(const std::chrono::microseconds& activeGrace, const std::chrono::microseconds& setupGrace) { auto currentTime = std::chrono::steady_clock::now(); auto elapsedMicros = std::chrono::duration_cast<std::chrono::microseconds>( currentTime - lastTime); State state = static_cast<session::State>(this->state()); switch (state) { case State::active: if (elapsedMicros < activeGrace) { return true; } break; case State::setupInProgress: if (elapsedMicros < setupGrace) { return true; } break; case State::tearDownInProgress: break; default: break; } return false; } /** * @brief Session's Requested Maximum Privilege Level */ Privilege reqMaxPrivLevel; /** * @brief session's user & channel access details */ ipmi::PrivAccess sessionUserPrivAccess{}; ipmi::ChannelAccess sessionChannelAccess{}; SequenceNumbers sequenceNums; // Session Sequence Numbers std::string userName{}; // User Name /** @brief Socket channel for communicating with the remote client.*/ std::shared_ptr<udpsocket::Channel> channelPtr; private: SessionID bmcSessionID = 0; // BMC Session ID SessionID remoteConsoleSessionID = 0; // Remote Console Session ID // Authentication Algorithm Interface for the Session std::unique_ptr<cipher::rakp_auth::Interface> authAlgoInterface; // Integrity Algorithm Interface for the Session std::unique_ptr<cipher::integrity::Interface> integrityAlgoInterface = nullptr; // Confidentiality Algorithm Interface for the Session std::unique_ptr<cipher::crypt::Interface> cryptAlgoInterface = nullptr; // Last Transaction Time decltype(std::chrono::steady_clock::now()) lastTime; }; } // namespace session