18bb10b79STom Joseph #include "rakp12.hpp" 28bb10b79STom Joseph 39e801a2bSVernon Mauery #include "comm_module.hpp" 49e801a2bSVernon Mauery #include "endian.hpp" 59e801a2bSVernon Mauery #include "guid.hpp" 69e801a2bSVernon Mauery #include "main.hpp" 79e801a2bSVernon Mauery 88bb10b79STom Joseph #include <openssl/rand.h> 98bb10b79STom Joseph 108bb10b79STom Joseph #include <algorithm> 1156527b93STom Joseph #include <cstring> 128bb10b79STom Joseph #include <iomanip> 13*fc37e59eSVernon Mauery #include <phosphor-logging/log.hpp> 14127748a8SRichard Marian Thomaiyar #include <user_channel/channel_layer.hpp> 15127748a8SRichard Marian Thomaiyar #include <user_channel/user_layer.hpp> 168bb10b79STom Joseph 17*fc37e59eSVernon Mauery using namespace phosphor::logging; 18*fc37e59eSVernon Mauery 198bb10b79STom Joseph namespace command 208bb10b79STom Joseph { 218bb10b79STom Joseph 2218a45e9dSTom Joseph std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload, 238bb10b79STom Joseph const message::Handler& handler) 248bb10b79STom Joseph { 258bb10b79STom Joseph std::vector<uint8_t> outPayload(sizeof(RAKP2response)); 2618a45e9dSTom Joseph auto request = reinterpret_cast<const RAKP1request*>(inPayload.data()); 278bb10b79STom Joseph auto response = reinterpret_cast<RAKP2response*>(outPayload.data()); 288bb10b79STom Joseph 298bb10b79STom Joseph // Session ID zero is reserved for Session Setup 308bb10b79STom Joseph if (endian::from_ipmi(request->managedSystemSessionID) == 318bb10b79STom Joseph session::SESSION_ZERO) 328bb10b79STom Joseph { 33*fc37e59eSVernon Mauery log<level::INFO>("RAKP12: BMC invalid Session ID"); 348bb10b79STom Joseph response->rmcpStatusCode = 358bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 368bb10b79STom Joseph return outPayload; 378bb10b79STom Joseph } 388bb10b79STom Joseph 398bb10b79STom Joseph std::shared_ptr<session::Session> session; 408bb10b79STom Joseph try 418bb10b79STom Joseph { 42ae1fda44SVernon Mauery session = 43ae1fda44SVernon Mauery std::get<session::Manager&>(singletonPool) 44ae1fda44SVernon Mauery .getSession(endian::from_ipmi(request->managedSystemSessionID)); 458bb10b79STom Joseph } 468bb10b79STom Joseph catch (std::exception& e) 478bb10b79STom Joseph { 48*fc37e59eSVernon Mauery log<level::ERR>("RAKP12 : session not found", 49*fc37e59eSVernon Mauery entry("EXCEPTION=%s", e.what())); 508bb10b79STom Joseph response->rmcpStatusCode = 518bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 528bb10b79STom Joseph return outPayload; 538bb10b79STom Joseph } 548bb10b79STom Joseph 559e801a2bSVernon Mauery auto rakp1Size = 569e801a2bSVernon Mauery sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len); 5756527b93STom Joseph 5856527b93STom Joseph // Validate user name length in the message 5956527b93STom Joseph if (request->user_name_len > userNameMaxLen || 6056527b93STom Joseph inPayload.size() != rakp1Size) 6156527b93STom Joseph { 6256527b93STom Joseph response->rmcpStatusCode = 6356527b93STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH); 6456527b93STom Joseph return outPayload; 6556527b93STom Joseph } 6656527b93STom Joseph 6756527b93STom Joseph session->userName.assign(request->user_name, request->user_name_len); 6856527b93STom Joseph 698bb10b79STom Joseph // Update transaction time 708bb10b79STom Joseph session->updateLastTransactionTime(); 718bb10b79STom Joseph 728bb10b79STom Joseph auto rcSessionID = endian::to_ipmi(session->getRCSessionID()); 738bb10b79STom Joseph auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID()); 748bb10b79STom Joseph auto authAlgo = session->getAuthAlgo(); 758bb10b79STom Joseph 768bb10b79STom Joseph /* 778bb10b79STom Joseph * Generate Key Authentication Code - RAKP 2 788bb10b79STom Joseph * 798bb10b79STom Joseph * 1) Remote Console Session ID - 4 bytes 808bb10b79STom Joseph * 2) Managed System Session ID - 4 bytes 818bb10b79STom Joseph * 3) Remote Console Random Number - 16 bytes 828bb10b79STom Joseph * 4) Managed System Random Number - 16 bytes 838bb10b79STom Joseph * 5) Managed System GUID - 16 bytes 848bb10b79STom Joseph * 6) Requested Privilege Level - 1 byte 858bb10b79STom Joseph * 7) User Name Length Byte - 1 byte (0 for 'null' username) 868bb10b79STom Joseph * 8) User Name - variable (absent for 'null' username) 878bb10b79STom Joseph */ 888bb10b79STom Joseph 898bb10b79STom Joseph std::vector<uint8_t> input; 908bb10b79STom Joseph input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) + 918bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN + 929e801a2bSVernon Mauery cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN + 939e801a2bSVernon Mauery sizeof(request->req_max_privilege_level) + 949e801a2bSVernon Mauery sizeof(request->user_name_len) + session->userName.size()); 958bb10b79STom Joseph 968bb10b79STom Joseph auto iter = input.begin(); 978bb10b79STom Joseph 988bb10b79STom Joseph // Remote Console Session ID 999e801a2bSVernon Mauery std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID), 1009e801a2bSVernon Mauery iter); 1018bb10b79STom Joseph std::advance(iter, sizeof(rcSessionID)); 1028bb10b79STom Joseph 1038bb10b79STom Joseph // Managed System Session ID 1048bb10b79STom Joseph std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID), 1058bb10b79STom Joseph iter); 1068bb10b79STom Joseph std::advance(iter, sizeof(bmcSessionID)); 1078bb10b79STom Joseph 1088bb10b79STom Joseph // Copy the Remote Console Random Number from the RAKP1 request to the 1098bb10b79STom Joseph // Authentication Algorithm 1109e801a2bSVernon Mauery std::copy_n( 1119e801a2bSVernon Mauery reinterpret_cast<const uint8_t*>(request->remote_console_random_number), 1128bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 1138bb10b79STom Joseph authAlgo->rcRandomNum.begin()); 1148bb10b79STom Joseph 1159e801a2bSVernon Mauery std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter); 1168bb10b79STom Joseph std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN); 1178bb10b79STom Joseph 1188bb10b79STom Joseph // Generate the Managed System Random Number 1198bb10b79STom Joseph if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) + 1208bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 1218bb10b79STom Joseph cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN)) 1228bb10b79STom Joseph { 1238bb10b79STom Joseph response->rmcpStatusCode = 1248bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE); 1258bb10b79STom Joseph return outPayload; 1268bb10b79STom Joseph } 1278bb10b79STom Joseph 128127748a8SRichard Marian Thomaiyar session->reqMaxPrivLevel = request->req_max_privilege_level; 129127748a8SRichard Marian Thomaiyar session->curPrivLevel = static_cast<session::Privilege>( 130127748a8SRichard Marian Thomaiyar request->req_max_privilege_level & session::reqMaxPrivMask); 131d91fd9d2SRichard Marian Thomaiyar if (request->user_name_len == 0) 132127748a8SRichard Marian Thomaiyar { 133d91fd9d2SRichard Marian Thomaiyar // Bail out, if user name is not specified. 134d91fd9d2SRichard Marian Thomaiyar // Yes, NULL user name is not supported for security reasons. 135127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 136127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 137127748a8SRichard Marian Thomaiyar return outPayload; 138127748a8SRichard Marian Thomaiyar } 139127748a8SRichard Marian Thomaiyar 140127748a8SRichard Marian Thomaiyar // Perform user name based lookup 141127748a8SRichard Marian Thomaiyar std::string userName(request->user_name, request->user_name_len); 142127748a8SRichard Marian Thomaiyar std::string passwd; 143127748a8SRichard Marian Thomaiyar uint8_t userId = ipmi::ipmiUserGetUserId(userName); 144127748a8SRichard Marian Thomaiyar if (userId == ipmi::invalidUserId) 145127748a8SRichard Marian Thomaiyar { 146127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 147127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 148127748a8SRichard Marian Thomaiyar return outPayload; 149127748a8SRichard Marian Thomaiyar } 150127748a8SRichard Marian Thomaiyar // check user is enabled before proceeding. 151127748a8SRichard Marian Thomaiyar bool userEnabled = false; 152127748a8SRichard Marian Thomaiyar ipmi::ipmiUserCheckEnabled(userId, userEnabled); 153127748a8SRichard Marian Thomaiyar if (!userEnabled) 154127748a8SRichard Marian Thomaiyar { 155127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 156127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 157127748a8SRichard Marian Thomaiyar return outPayload; 158127748a8SRichard Marian Thomaiyar } 159127748a8SRichard Marian Thomaiyar // Get the user password for RAKP message authenticate 160127748a8SRichard Marian Thomaiyar passwd = ipmi::ipmiUserGetPassword(userName); 161127748a8SRichard Marian Thomaiyar if (passwd.empty()) 162127748a8SRichard Marian Thomaiyar { 163127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 164127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 165127748a8SRichard Marian Thomaiyar return outPayload; 166127748a8SRichard Marian Thomaiyar } 167127748a8SRichard Marian Thomaiyar ipmi::PrivAccess userAccess{}; 168127748a8SRichard Marian Thomaiyar ipmi::ChannelAccess chAccess{}; 169127748a8SRichard Marian Thomaiyar // TODO Replace with proper calls. 170127748a8SRichard Marian Thomaiyar uint8_t chNum = static_cast<uint8_t>(ipmi::EChannelID::chanLan1); 171127748a8SRichard Marian Thomaiyar // Get channel based access information 172127748a8SRichard Marian Thomaiyar if ((ipmi::ipmiUserGetPrivilegeAccess(userId, chNum, userAccess) != 173127748a8SRichard Marian Thomaiyar IPMI_CC_OK) || 174127748a8SRichard Marian Thomaiyar (ipmi::getChannelAccessData(chNum, chAccess) != IPMI_CC_OK)) 175127748a8SRichard Marian Thomaiyar { 176127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 177127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 178127748a8SRichard Marian Thomaiyar return outPayload; 179127748a8SRichard Marian Thomaiyar } 180127748a8SRichard Marian Thomaiyar session->chNum = chNum; 181127748a8SRichard Marian Thomaiyar // minimum privilege of Channel / User / requested has to be used 182127748a8SRichard Marian Thomaiyar // as session current privilege level 183127748a8SRichard Marian Thomaiyar uint8_t minPriv = 0; 184127748a8SRichard Marian Thomaiyar if (chAccess.privLimit < userAccess.privilege) 185127748a8SRichard Marian Thomaiyar { 186127748a8SRichard Marian Thomaiyar minPriv = chAccess.privLimit; 187127748a8SRichard Marian Thomaiyar } 188127748a8SRichard Marian Thomaiyar else 189127748a8SRichard Marian Thomaiyar { 190127748a8SRichard Marian Thomaiyar minPriv = userAccess.privilege; 191127748a8SRichard Marian Thomaiyar } 192127748a8SRichard Marian Thomaiyar if (session->curPrivLevel > static_cast<session::Privilege>(minPriv)) 193127748a8SRichard Marian Thomaiyar { 194127748a8SRichard Marian Thomaiyar session->curPrivLevel = static_cast<session::Privilege>(minPriv); 195127748a8SRichard Marian Thomaiyar } 196d91fd9d2SRichard Marian Thomaiyar // For username / privilege lookup, fail with UNAUTH_NAME, if requested 197d91fd9d2SRichard Marian Thomaiyar // max privilege is greater than the user privilege. 198d91fd9d2SRichard Marian Thomaiyar if (((request->req_max_privilege_level & userNameOnlyLookupMask) == 199d91fd9d2SRichard Marian Thomaiyar userNamePrivLookup) && 200d91fd9d2SRichard Marian Thomaiyar ((request->req_max_privilege_level & session::reqMaxPrivMask) > 201d91fd9d2SRichard Marian Thomaiyar userAccess.privilege)) 202d91fd9d2SRichard Marian Thomaiyar { 203*fc37e59eSVernon Mauery log<level::INFO>( 204*fc37e59eSVernon Mauery "Username/Privilege lookup failed for requested privilege"); 205d91fd9d2SRichard Marian Thomaiyar response->rmcpStatusCode = 206d91fd9d2SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 207d91fd9d2SRichard Marian Thomaiyar return outPayload; 208d91fd9d2SRichard Marian Thomaiyar } 209127748a8SRichard Marian Thomaiyar 210127748a8SRichard Marian Thomaiyar std::fill(authAlgo->userKey.data(), 211127748a8SRichard Marian Thomaiyar authAlgo->userKey.data() + authAlgo->userKey.size(), 0); 212127748a8SRichard Marian Thomaiyar std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data()); 21399b87849SRichard Marian Thomaiyar 2148bb10b79STom Joseph // Copy the Managed System Random Number to the Authentication Algorithm 2158bb10b79STom Joseph std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN, 2168bb10b79STom Joseph authAlgo->bmcRandomNum.begin()); 2178bb10b79STom Joseph std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN); 2188bb10b79STom Joseph 2198bb10b79STom Joseph // Managed System GUID 22083029cb8STom Joseph std::copy_n(cache::guid.data(), cache::guid.size(), iter); 2218bb10b79STom Joseph std::advance(iter, BMC_GUID_LEN); 2228bb10b79STom Joseph 2238bb10b79STom Joseph // Requested Privilege Level 2248bb10b79STom Joseph std::copy_n(&(request->req_max_privilege_level), 2258bb10b79STom Joseph sizeof(request->req_max_privilege_level), iter); 2268bb10b79STom Joseph std::advance(iter, sizeof(request->req_max_privilege_level)); 2278bb10b79STom Joseph 2288bb10b79STom Joseph // User Name Length Byte 2298bb10b79STom Joseph std::copy_n(&(request->user_name_len), sizeof(request->user_name_len), 2308bb10b79STom Joseph iter); 23156527b93STom Joseph std::advance(iter, sizeof(request->user_name_len)); 23256527b93STom Joseph 23356527b93STom Joseph std::copy_n(session->userName.data(), session->userName.size(), iter); 2348bb10b79STom Joseph 2358bb10b79STom Joseph // Generate Key Exchange Authentication Code - RAKP2 2368bb10b79STom Joseph auto output = authAlgo->generateHMAC(input); 2378bb10b79STom Joseph 2388bb10b79STom Joseph response->messageTag = request->messageTag; 2398bb10b79STom Joseph response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR); 2408bb10b79STom Joseph response->reserved = 0; 2418bb10b79STom Joseph response->remoteConsoleSessionID = rcSessionID; 2428bb10b79STom Joseph 2438bb10b79STom Joseph // Copy Managed System Random Number to the Response 2448bb10b79STom Joseph std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(), 2458bb10b79STom Joseph response->managed_system_random_number); 2468bb10b79STom Joseph 2478bb10b79STom Joseph // Copy System GUID to the Response 2489e801a2bSVernon Mauery std::copy_n(cache::guid.data(), cache::guid.size(), 24983029cb8STom Joseph response->managed_system_guid); 2508bb10b79STom Joseph 2518bb10b79STom Joseph // Insert the HMAC output into the payload 2528bb10b79STom Joseph outPayload.insert(outPayload.end(), output.begin(), output.end()); 2538bb10b79STom Joseph return outPayload; 2548bb10b79STom Joseph } 2558bb10b79STom Joseph 2568bb10b79STom Joseph } // namespace command 257