From 127748a82ce1459a35d9886dc250697e78c24dd2 Mon Sep 17 00:00:00 2001 From: Richard Marian Thomaiyar Date: Thu, 6 Sep 2018 07:08:51 +0530 Subject: 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 --- Makefile.am | 1 + command/rakp12.cpp | 90 ++++++++++++++++++++++++++++++++++++++++-------- command/rakp12.hpp | 3 ++ command/rakp34.cpp | 2 +- command/session_cmds.cpp | 46 ++++++++++++++++++++++--- session.hpp | 11 ++++-- 6 files changed, 131 insertions(+), 22 deletions(-) diff --git a/Makefile.am b/Makefile.am index 5ed3eec..352a20c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -65,6 +65,7 @@ netipmid_LDFLAGS = \ $(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 #include #include +#include +#include namespace command { @@ -61,15 +63,6 @@ std::vector RAKP12(const std::vector& inPayload, 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(RAKP_ReturnCode::UNAUTH_NAME); - return outPayload; - } - // Update transaction time session->updateLastTransactionTime(); @@ -129,6 +122,80 @@ std::vector RAKP12(const std::vector& inPayload, return outPayload; } + session->reqMaxPrivLevel = request->req_max_privilege_level; + session->curPrivLevel = static_cast( + 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(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(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(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(RAKP_ReturnCode::UNAUTH_NAME); + return outPayload; + } + ipmi::PrivAccess userAccess{}; + ipmi::ChannelAccess chAccess{}; + // TODO Replace with proper calls. + uint8_t chNum = static_cast(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(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(minPriv)) + { + session->curPrivLevel = static_cast(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::vector RAKP12(const std::vector& inPayload, std::advance(iter, BMC_GUID_LEN); // Requested Privilege Level - session->curPrivLevel = - static_cast(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 @@ namespace command 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 @@ std::vector RAKP34(const std::vector& inPayload, auto rcSessionID = endian::to_ipmi(session->getRCSessionID()); // Session Privilege Level - auto sessPrivLevel = static_cast(session->curPrivLevel); + auto sessPrivLevel = session->reqMaxPrivLevel; // User Name Length Byte auto userLength = static_cast(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 #include +#include +#include namespace command { @@ -29,18 +31,54 @@ std::vector if (reqPrivilegeLevel == 0) // Just return present privilege level { response->newPrivLevel = static_cast(session->curPrivLevel); + return outPayload; } - else if (reqPrivilegeLevel <= static_cast(session->maxPrivLevel)) + if (reqPrivilegeLevel > + (session->reqMaxPrivLevel & session::reqMaxPrivMask)) { - session->curPrivLevel = - static_cast(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(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 @@ constexpr auto SESSION_SETUP_TIMEOUT = 5s; // 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 @@ class Session /** * @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 @@ class Session /** @brief Socket channel for communicating with the remote client.*/ std::shared_ptr channelPtr; + uint8_t chNum; private: SessionID bmcSessionID = 0; // BMC Session ID -- cgit v1.2.1