blob: 84c90fcecb05079ad9ec77e102075a75f84bc1f2 [file] [log] [blame]
#include "rakp34.hpp"
#include "comm_module.hpp"
#include "endian.hpp"
#include "guid.hpp"
#include "main.hpp"
#include "rmcp.hpp"
#include <algorithm>
#include <cstring>
#include <iostream>
namespace command
{
void applyIntegrityAlgo(const uint32_t bmcSessionID)
{
auto session =
std::get<session::Manager&>(singletonPool).getSession(bmcSessionID);
auto authAlgo = session->getAuthAlgo();
switch (authAlgo->intAlgo)
{
case cipher::integrity::Algorithms::HMAC_SHA1_96:
{
session->setIntegrityAlgo(
std::make_unique<cipher::integrity::AlgoSHA1>(
authAlgo->sessionIntegrityKey));
break;
}
case cipher::integrity::Algorithms::HMAC_SHA256_128:
{
session->setIntegrityAlgo(
std::make_unique<cipher::integrity::AlgoSHA256>(
authAlgo->sessionIntegrityKey));
break;
}
default:
break;
}
}
void applyCryptAlgo(const uint32_t bmcSessionID)
{
auto session =
std::get<session::Manager&>(singletonPool).getSession(bmcSessionID);
auto authAlgo = session->getAuthAlgo();
switch (authAlgo->cryptAlgo)
{
case cipher::crypt::Algorithms::AES_CBC_128:
{
auto intAlgo = session->getIntegrityAlgo();
auto k2 = intAlgo->generateKn(authAlgo->sessionIntegrityKey,
rmcp::const_2);
session->setCryptAlgo(
std::make_unique<cipher::crypt::AlgoAES128>(k2));
break;
}
default:
break;
}
}
std::vector<uint8_t> RAKP34(const std::vector<uint8_t>& inPayload,
const message::Handler& handler)
{
std::vector<uint8_t> outPayload(sizeof(RAKP4response));
auto request = reinterpret_cast<const RAKP3request*>(inPayload.data());
auto response = reinterpret_cast<RAKP4response*>(outPayload.data());
// Check if the RAKP3 Payload Length is as expected
if (inPayload.size() < sizeof(RAKP3request))
{
std::cerr << "RAKP34: Invalid RAKP3 request\n";
response->rmcpStatusCode =
static_cast<uint8_t>(RAKP_ReturnCode::INVALID_INTEGRITY_VALUE);
return outPayload;
}
// Session ID zero is reserved for Session Setup
if (endian::from_ipmi(request->managedSystemSessionID) ==
session::SESSION_ZERO)
{
std::cerr << "RAKP34: BMC invalid Session ID\n";
response->rmcpStatusCode =
static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
return outPayload;
}
std::shared_ptr<session::Session> session;
try
{
session =
std::get<session::Manager&>(singletonPool)
.getSession(endian::from_ipmi(request->managedSystemSessionID));
}
catch (std::exception& e)
{
std::cerr << e.what() << "\n";
response->rmcpStatusCode =
static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
return outPayload;
}
session->updateLastTransactionTime();
auto authAlgo = session->getAuthAlgo();
/*
* Key Authentication Code - RAKP 3
*
* 1) Managed System Random Number - 16 bytes
* 2) Remote Console Session ID - 4 bytes
* 3) Session Privilege Level - 1 byte
* 4) User Name Length Byte - 1 byte (0 for 'null' username)
* 5) User Name - variable (absent for 'null' username)
*/
// Remote Console Session ID
auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
// Session Privilege Level
auto sessPrivLevel = static_cast<uint8_t>(session->curPrivLevel);
// User Name Length Byte
auto userLength = static_cast<uint8_t>(session->userName.size());
std::vector<uint8_t> input;
input.resize(cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
sizeof(rcSessionID) + sizeof(sessPrivLevel) +
sizeof(userLength) + userLength);
auto iter = input.begin();
// Managed System Random Number
std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
iter);
std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
// Remote Console Session ID
std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
iter);
std::advance(iter, sizeof(rcSessionID));
// Session Privilege Level
std::copy_n(reinterpret_cast<uint8_t*>(&sessPrivLevel),
sizeof(sessPrivLevel), iter);
std::advance(iter, sizeof(sessPrivLevel));
// User Name Length Byte
std::copy_n(&userLength, sizeof(userLength), iter);
std::advance(iter, sizeof(userLength));
std::copy_n(session->userName.data(), userLength, iter);
// Generate Key Exchange Authentication Code - RAKP2
auto output = authAlgo->generateHMAC(input);
if (inPayload.size() != (sizeof(RAKP3request) + output.size()) ||
std::memcmp(output.data(), request + 1, output.size()))
{
std::cerr << "Mismatch in HMAC sent by remote console\n";
response->messageTag = request->messageTag;
response->rmcpStatusCode =
static_cast<uint8_t>(RAKP_ReturnCode::INVALID_INTEGRITY_VALUE);
response->reserved = 0;
response->remoteConsoleSessionID = rcSessionID;
// close the session
std::get<session::Manager&>(singletonPool)
.stopSession(session->getBMCSessionID());
return outPayload;
}
/*
* Session Integrity Key
*
* 1) Remote Console Random Number - 16 bytes
* 2) Managed System Random Number - 16 bytes
* 3) Session Privilege Level - 1 byte
* 4) User Name Length Byte - 1 byte (0 for 'null' username)
* 5) User Name - variable (absent for 'null' username)
*/
input.clear();
input.resize(cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
sizeof(sessPrivLevel) + sizeof(userLength) + userLength);
iter = input.begin();
// Remote Console Random Number
std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
// Managed Console Random Number
std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
iter);
std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
// Session Privilege Level
std::copy_n(reinterpret_cast<uint8_t*>(&sessPrivLevel),
sizeof(sessPrivLevel), iter);
std::advance(iter, sizeof(sessPrivLevel));
// User Name Length Byte
std::copy_n(&userLength, sizeof(userLength), iter);
std::advance(iter, sizeof(userLength));
std::copy_n(session->userName.data(), userLength, iter);
// Generate Session Integrity Key
auto sikOutput = authAlgo->generateHMAC(input);
// Update the SIK in the Authentication Algo Interface
authAlgo->sessionIntegrityKey.insert(authAlgo->sessionIntegrityKey.begin(),
sikOutput.begin(), sikOutput.end());
/*
* Integrity Check Value
*
* 1) Remote Console Random Number - 16 bytes
* 2) Managed System Session ID - 4 bytes
* 3) Managed System GUID - 16 bytes
*/
// Get Managed System Session ID
auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
input.clear();
input.resize(cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
sizeof(bmcSessionID) + BMC_GUID_LEN);
iter = input.begin();
// Remote Console Random Number
std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
// Managed System Session ID
std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
iter);
std::advance(iter, sizeof(bmcSessionID));
// Managed System GUID
std::copy_n(cache::guid.data(), cache::guid.size(), iter);
// Integrity Check Value
auto icv = authAlgo->generateICV(input);
outPayload.resize(sizeof(RAKP4response));
response->messageTag = request->messageTag;
response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
response->reserved = 0;
response->remoteConsoleSessionID = rcSessionID;
// Insert the HMAC output into the payload
outPayload.insert(outPayload.end(), icv.begin(), icv.end());
// Set the Integrity Algorithm
applyIntegrityAlgo(session->getBMCSessionID());
// Set the Confidentiality Algorithm
applyCryptAlgo(session->getBMCSessionID());
session->state = session::State::ACTIVE;
return outPayload;
}
} // namespace command