blob: 5ddf2f79a06771dee738c7343c1db5453318f1bb [file] [log] [blame]
#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() = default;
~Session() = default;
Session(const Session&) = delete;
Session& operator=(const Session&) = delete;
Session(Session&&) = default;
Session& operator=(Session&&) = default;
/**
* @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