RMCP+ login support with privilege
Implementation of RMCP login support with appropriate
privilege level.
Unit Test:
1. Verified that user is able to login without any issues
2. Privilege of the user is minimum of requested, user & channel
3. Unable to set higher privilege using Set session commands
Change-Id: I5e9ef21dfc1f1b50aa815562a3a65d90c434877c
Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>
diff --git a/Makefile.am b/Makefile.am
index 5ed3eec..352a20c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -65,6 +65,7 @@
$(PHOSPHOR_LOGGING_LIBS) \
$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
$(LIBADD_DLOPEN) \
+ -luserlayer \
-export-dynamic
netipmid_CXXFLAGS = \
diff --git a/command/rakp12.cpp b/command/rakp12.cpp
index b4842b2..5384ab3 100644
--- a/command/rakp12.cpp
+++ b/command/rakp12.cpp
@@ -11,6 +11,8 @@
#include <cstring>
#include <iomanip>
#include <iostream>
+#include <user_channel/channel_layer.hpp>
+#include <user_channel/user_layer.hpp>
namespace command
{
@@ -61,15 +63,6 @@
session->userName.assign(request->user_name, request->user_name_len);
- // Validate the user name if the username is provided
- if (request->user_name_len &&
- (session->userName != cipher::rakp_auth::userName))
- {
- response->rmcpStatusCode =
- static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
- return outPayload;
- }
-
// Update transaction time
session->updateLastTransactionTime();
@@ -129,6 +122,80 @@
return outPayload;
}
+ session->reqMaxPrivLevel = request->req_max_privilege_level;
+ session->curPrivLevel = static_cast<session::Privilege>(
+ request->req_max_privilege_level & session::reqMaxPrivMask);
+ if (((request->req_max_privilege_level & userNameOnlyLookupMask) !=
+ userNameOnlyLookup) ||
+ (request->user_name_len == 0))
+ {
+ // Skip privilege based lookup for security purpose
+ response->rmcpStatusCode =
+ static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
+ return outPayload;
+ }
+
+ // Perform user name based lookup
+ std::string userName(request->user_name, request->user_name_len);
+ std::string passwd;
+ uint8_t userId = ipmi::ipmiUserGetUserId(userName);
+ if (userId == ipmi::invalidUserId)
+ {
+ response->rmcpStatusCode =
+ static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
+ return outPayload;
+ }
+ // check user is enabled before proceeding.
+ bool userEnabled = false;
+ ipmi::ipmiUserCheckEnabled(userId, userEnabled);
+ if (!userEnabled)
+ {
+ response->rmcpStatusCode =
+ static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
+ return outPayload;
+ }
+ // Get the user password for RAKP message authenticate
+ passwd = ipmi::ipmiUserGetPassword(userName);
+ if (passwd.empty())
+ {
+ response->rmcpStatusCode =
+ static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
+ return outPayload;
+ }
+ ipmi::PrivAccess userAccess{};
+ ipmi::ChannelAccess chAccess{};
+ // TODO Replace with proper calls.
+ uint8_t chNum = static_cast<uint8_t>(ipmi::EChannelID::chanLan1);
+ // Get channel based access information
+ if ((ipmi::ipmiUserGetPrivilegeAccess(userId, chNum, userAccess) !=
+ IPMI_CC_OK) ||
+ (ipmi::getChannelAccessData(chNum, chAccess) != IPMI_CC_OK))
+ {
+ response->rmcpStatusCode =
+ static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
+ return outPayload;
+ }
+ session->chNum = chNum;
+ // minimum privilege of Channel / User / requested has to be used
+ // as session current privilege level
+ uint8_t minPriv = 0;
+ if (chAccess.privLimit < userAccess.privilege)
+ {
+ minPriv = chAccess.privLimit;
+ }
+ else
+ {
+ minPriv = userAccess.privilege;
+ }
+ if (session->curPrivLevel > static_cast<session::Privilege>(minPriv))
+ {
+ session->curPrivLevel = static_cast<session::Privilege>(minPriv);
+ }
+
+ std::fill(authAlgo->userKey.data(),
+ authAlgo->userKey.data() + authAlgo->userKey.size(), 0);
+ std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data());
+
// Copy the Managed System Random Number to the Authentication Algorithm
std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN,
authAlgo->bmcRandomNum.begin());
@@ -139,15 +206,10 @@
std::advance(iter, BMC_GUID_LEN);
// Requested Privilege Level
- session->curPrivLevel =
- static_cast<session::Privilege>(request->req_max_privilege_level);
std::copy_n(&(request->req_max_privilege_level),
sizeof(request->req_max_privilege_level), iter);
std::advance(iter, sizeof(request->req_max_privilege_level));
- // Set Max Privilege to ADMIN
- session->maxPrivLevel = session::Privilege::ADMIN;
-
// User Name Length Byte
std::copy_n(&(request->user_name_len), sizeof(request->user_name_len),
iter);
diff --git a/command/rakp12.hpp b/command/rakp12.hpp
index 1ce533a..95124be 100644
--- a/command/rakp12.hpp
+++ b/command/rakp12.hpp
@@ -10,6 +10,9 @@
constexpr size_t userNameMaxLen = 16;
+constexpr uint8_t userNameOnlyLookupMask = 0x10;
+constexpr uint8_t userNameOnlyLookup = 0x10;
+
/**
* @struct RAKP1request
*
diff --git a/command/rakp34.cpp b/command/rakp34.cpp
index 84c90fc..a89e750 100644
--- a/command/rakp34.cpp
+++ b/command/rakp34.cpp
@@ -123,7 +123,7 @@
auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
// Session Privilege Level
- auto sessPrivLevel = static_cast<uint8_t>(session->curPrivLevel);
+ auto sessPrivLevel = session->reqMaxPrivLevel;
// User Name Length Byte
auto userLength = static_cast<uint8_t>(session->userName.size());
diff --git a/command/session_cmds.cpp b/command/session_cmds.cpp
index d363c1e..5c74d28 100644
--- a/command/session_cmds.cpp
+++ b/command/session_cmds.cpp
@@ -6,6 +6,8 @@
#include <host-ipmid/ipmid-api.h>
#include <iostream>
+#include <user_channel/channel_layer.hpp>
+#include <user_channel/user_layer.hpp>
namespace command
{
@@ -29,18 +31,54 @@
if (reqPrivilegeLevel == 0) // Just return present privilege level
{
response->newPrivLevel = static_cast<uint8_t>(session->curPrivLevel);
+ return outPayload;
}
- else if (reqPrivilegeLevel <= static_cast<uint8_t>(session->maxPrivLevel))
+ if (reqPrivilegeLevel >
+ (session->reqMaxPrivLevel & session::reqMaxPrivMask))
{
- session->curPrivLevel =
- static_cast<session::Privilege>(reqPrivilegeLevel);
- response->newPrivLevel = reqPrivilegeLevel;
+ // Requested level exceeds Channel and/or User Privilege Limit
+ response->completionCode = IPMI_CC_EXCEEDS_USER_PRIV;
+ return outPayload;
+ }
+
+ uint8_t userId = ipmi::ipmiUserGetUserId(session->userName);
+ if (userId == ipmi::invalidUserId)
+ {
+ response->completionCode = IPMI_CC_UNSPECIFIED_ERROR;
+ return outPayload;
+ }
+ ipmi::PrivAccess userAccess{};
+ ipmi::ChannelAccess chAccess{};
+ if ((ipmi::ipmiUserGetPrivilegeAccess(userId, session->chNum, userAccess) !=
+ IPMI_CC_OK) ||
+ (ipmi::getChannelAccessData(session->chNum, chAccess) != IPMI_CC_OK))
+ {
+ response->completionCode = IPMI_CC_INVALID_PRIV_LEVEL;
+ return outPayload;
+ }
+ // Use the minimum privilege of user or channel
+ uint8_t minPriv = 0;
+ if (chAccess.privLimit < userAccess.privilege)
+ {
+ minPriv = chAccess.privLimit;
}
else
{
+ minPriv = userAccess.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->curPrivLevel =
+ static_cast<session::Privilege>(reqPrivilegeLevel);
+ response->newPrivLevel = reqPrivilegeLevel;
+ }
+
return outPayload;
}
diff --git a/session.hpp b/session.hpp
index f3dc2ae..bf136e3 100644
--- a/session.hpp
+++ b/session.hpp
@@ -43,6 +43,10 @@
// Seconds of inactivity allowed when session is active
constexpr auto SESSION_INACTIVITY_TIMEOUT = 60s;
+// Mask to get only the privilege from requested maximum privlege (RAKP message
+// 1)
+constexpr uint8_t reqMaxPrivMask = 0xF;
+
/**
* @struct SequenceNumbers Session Sequence Numbers
*
@@ -261,12 +265,12 @@
/**
* @brief Session's Current Privilege Level
*/
- Privilege curPrivLevel;
+ Privilege curPrivLevel = Privilege::CALLBACK;
/**
- * @brief Session's Maximum Privilege Level
+ * @brief Session's Requested Maximum Privilege Level
*/
- Privilege maxPrivLevel = Privilege::CALLBACK;
+ uint8_t reqMaxPrivLevel;
SequenceNumbers sequenceNums; // Session Sequence Numbers
State state = State::INACTIVE; // Session State
@@ -274,6 +278,7 @@
/** @brief Socket channel for communicating with the remote client.*/
std::shared_ptr<udpsocket::Channel> channelPtr;
+ uint8_t chNum;
private:
SessionID bmcSessionID = 0; // BMC Session ID