18bb10b79STom Joseph #include "rakp12.hpp" 28bb10b79STom Joseph 39e801a2bSVernon Mauery #include "comm_module.hpp" 49e801a2bSVernon Mauery #include "endian.hpp" 59e801a2bSVernon Mauery #include "guid.hpp" 62085ae07SVernon Mauery #include "sessions_manager.hpp" 79e801a2bSVernon Mauery 88bb10b79STom Joseph #include <openssl/rand.h> 98bb10b79STom Joseph 10bc8958feSGeorge Liu #include <ipmid/types.hpp> 11*7b7f25f7SGeorge Liu #include <phosphor-logging/lg2.hpp> 12bc8958feSGeorge Liu 138bb10b79STom Joseph #include <algorithm> 1456527b93STom Joseph #include <cstring> 158bb10b79STom Joseph #include <iomanip> 168bb10b79STom Joseph 178bb10b79STom Joseph namespace command 188bb10b79STom Joseph { 198bb10b79STom Joseph 20ecb32fbcSAppaRao Puli bool isChannelAccessModeEnabled(const uint8_t accessMode) 21ecb32fbcSAppaRao Puli { 22ecb32fbcSAppaRao Puli return accessMode != 23ecb32fbcSAppaRao Puli static_cast<uint8_t>(ipmi::EChannelAccessMode::disabled); 24ecb32fbcSAppaRao Puli } 25ecb32fbcSAppaRao Puli 260e0546f1Ssunitakx void logInvalidLoginRedfishEvent(const std::string& journalMsg, 270e0546f1Ssunitakx const std::optional<std::string>& messageArgs) 280e0546f1Ssunitakx { 290e0546f1Ssunitakx static constexpr std::string_view openBMCMessageRegistryVersion = "0.1."; 300e0546f1Ssunitakx std::string messageID = "OpenBMC." + 310e0546f1Ssunitakx std::string(openBMCMessageRegistryVersion) + 320e0546f1Ssunitakx "InvalidLoginAttempted"; 33*7b7f25f7SGeorge Liu lg2::error( 34*7b7f25f7SGeorge Liu "message: {MSG}, id: {REDFISH_MESSAGE_ID}, args: {REDFISH_MESSAGE_ARGS}", 35*7b7f25f7SGeorge Liu "MSG", journalMsg, "REDFISH_MESSAGE_ID", messageID, 36*7b7f25f7SGeorge Liu "REDFISH_MESSAGE_ARGS", messageArgs.value()); 370e0546f1Ssunitakx } 3818a45e9dSTom Joseph std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload, 39be1470ccSGeorge Liu std::shared_ptr<message::Handler>& /* handler */) 408bb10b79STom Joseph { 4118a45e9dSTom Joseph auto request = reinterpret_cast<const RAKP1request*>(inPayload.data()); 422b1edef0SZhikui Ren // verify inPayload minimum size 432b1edef0SZhikui Ren if (inPayload.size() < (sizeof(*request) - userNameMaxLen)) 442b1edef0SZhikui Ren { 452b1edef0SZhikui Ren std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID}; 462b1edef0SZhikui Ren return errorPayload; 472b1edef0SZhikui Ren } 482b1edef0SZhikui Ren 492b1edef0SZhikui Ren std::vector<uint8_t> outPayload(sizeof(RAKP2response)); 508bb10b79STom Joseph auto response = reinterpret_cast<RAKP2response*>(outPayload.data()); 518bb10b79STom Joseph 528bb10b79STom Joseph // Session ID zero is reserved for Session Setup 538bb10b79STom Joseph if (endian::from_ipmi(request->managedSystemSessionID) == 54f8a34fc4SSuryakanth Sekar session::sessionZero) 558bb10b79STom Joseph { 56*7b7f25f7SGeorge Liu lg2::info("RAKP12: BMC invalid Session ID"); 578bb10b79STom Joseph response->rmcpStatusCode = 588bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 598bb10b79STom Joseph return outPayload; 608bb10b79STom Joseph } 618bb10b79STom Joseph 628bb10b79STom Joseph std::shared_ptr<session::Session> session; 638bb10b79STom Joseph try 648bb10b79STom Joseph { 652085ae07SVernon Mauery session = session::Manager::get().getSession( 662085ae07SVernon Mauery endian::from_ipmi(request->managedSystemSessionID)); 678bb10b79STom Joseph } 6812d199b2SPatrick Williams catch (const std::exception& e) 698bb10b79STom Joseph { 70*7b7f25f7SGeorge Liu lg2::error("RAKP12 : session not found: {ERROR}", "ERROR", e); 718bb10b79STom Joseph response->rmcpStatusCode = 728bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 738bb10b79STom Joseph return outPayload; 748bb10b79STom Joseph } 758bb10b79STom Joseph 769e801a2bSVernon Mauery auto rakp1Size = 779e801a2bSVernon Mauery sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len); 7856527b93STom Joseph 790e0546f1Ssunitakx std::string message = "Invalid login attempted via RCMPP interface "; 8056527b93STom Joseph // Validate user name length in the message 8156527b93STom Joseph if (request->user_name_len > userNameMaxLen || 8256527b93STom Joseph inPayload.size() != rakp1Size) 8356527b93STom Joseph { 8456527b93STom Joseph response->rmcpStatusCode = 8556527b93STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH); 860e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 8756527b93STom Joseph return outPayload; 8856527b93STom Joseph } 8956527b93STom Joseph 9056527b93STom Joseph session->userName.assign(request->user_name, request->user_name_len); 9156527b93STom Joseph 928bb10b79STom Joseph // Update transaction time 938bb10b79STom Joseph session->updateLastTransactionTime(); 948bb10b79STom Joseph 958bb10b79STom Joseph auto rcSessionID = endian::to_ipmi(session->getRCSessionID()); 968bb10b79STom Joseph auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID()); 978bb10b79STom Joseph auto authAlgo = session->getAuthAlgo(); 988bb10b79STom Joseph 998bb10b79STom Joseph /* 1008bb10b79STom Joseph * Generate Key Authentication Code - RAKP 2 1018bb10b79STom Joseph * 1028bb10b79STom Joseph * 1) Remote Console Session ID - 4 bytes 1038bb10b79STom Joseph * 2) Managed System Session ID - 4 bytes 1048bb10b79STom Joseph * 3) Remote Console Random Number - 16 bytes 1058bb10b79STom Joseph * 4) Managed System Random Number - 16 bytes 1068bb10b79STom Joseph * 5) Managed System GUID - 16 bytes 1078bb10b79STom Joseph * 6) Requested Privilege Level - 1 byte 1088bb10b79STom Joseph * 7) User Name Length Byte - 1 byte (0 for 'null' username) 1098bb10b79STom Joseph * 8) User Name - variable (absent for 'null' username) 1108bb10b79STom Joseph */ 1118bb10b79STom Joseph 1128bb10b79STom Joseph std::vector<uint8_t> input; 1138bb10b79STom Joseph input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) + 1148bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN + 1159e801a2bSVernon Mauery cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN + 1169e801a2bSVernon Mauery sizeof(request->req_max_privilege_level) + 1179e801a2bSVernon Mauery sizeof(request->user_name_len) + session->userName.size()); 1188bb10b79STom Joseph 1198bb10b79STom Joseph auto iter = input.begin(); 1208bb10b79STom Joseph 1218bb10b79STom Joseph // Remote Console Session ID 1229e801a2bSVernon Mauery std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID), 1239e801a2bSVernon Mauery iter); 1248bb10b79STom Joseph std::advance(iter, sizeof(rcSessionID)); 1258bb10b79STom Joseph 1268bb10b79STom Joseph // Managed System Session ID 1278bb10b79STom Joseph std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID), 1288bb10b79STom Joseph iter); 1298bb10b79STom Joseph std::advance(iter, sizeof(bmcSessionID)); 1308bb10b79STom Joseph 1318bb10b79STom Joseph // Copy the Remote Console Random Number from the RAKP1 request to the 1328bb10b79STom Joseph // Authentication Algorithm 1339e801a2bSVernon Mauery std::copy_n( 1349e801a2bSVernon Mauery reinterpret_cast<const uint8_t*>(request->remote_console_random_number), 1358bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 1368bb10b79STom Joseph authAlgo->rcRandomNum.begin()); 1378bb10b79STom Joseph 1389e801a2bSVernon Mauery std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter); 1398bb10b79STom Joseph std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN); 1408bb10b79STom Joseph 1418bb10b79STom Joseph // Generate the Managed System Random Number 1428bb10b79STom Joseph if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) + 1438bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 1448bb10b79STom Joseph cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN)) 1458bb10b79STom Joseph { 1468bb10b79STom Joseph response->rmcpStatusCode = 1478bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE); 1488bb10b79STom Joseph return outPayload; 1498bb10b79STom Joseph } 150d5a4f45eSRichard Marian Thomaiyar // As stated in Set Session Privilege Level command in IPMI Spec, when 1512555e2ecSjayaprakash Mutyala // creating a session through Activate command / RAKP 1 message, it must 1522555e2ecSjayaprakash Mutyala // be established with USER privilege as well as all other sessions are 1532555e2ecSjayaprakash Mutyala // initially set to USER privilege, regardless of the requested maximum 1542555e2ecSjayaprakash Mutyala // privilege. 1552555e2ecSjayaprakash Mutyala if (!(static_cast<session::Privilege>(request->req_max_privilege_level & 156d5a4f45eSRichard Marian Thomaiyar session::reqMaxPrivMask) > 1572555e2ecSjayaprakash Mutyala session::Privilege::CALLBACK)) 158d5a4f45eSRichard Marian Thomaiyar { 1592555e2ecSjayaprakash Mutyala response->rmcpStatusCode = 1602555e2ecSjayaprakash Mutyala static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_ROLE_PRIV); 1612555e2ecSjayaprakash Mutyala return outPayload; 162d5a4f45eSRichard Marian Thomaiyar } 1632555e2ecSjayaprakash Mutyala session->currentPrivilege(static_cast<uint8_t>(session::Privilege::USER)); 1642555e2ecSjayaprakash Mutyala 1654021b1f7STom Joseph session->reqMaxPrivLevel = 1664021b1f7STom Joseph static_cast<session::Privilege>(request->req_max_privilege_level); 167d91fd9d2SRichard Marian Thomaiyar if (request->user_name_len == 0) 168127748a8SRichard Marian Thomaiyar { 169d91fd9d2SRichard Marian Thomaiyar // Bail out, if user name is not specified. 170d91fd9d2SRichard Marian Thomaiyar // Yes, NULL user name is not supported for security reasons. 171127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 172127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 1730e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 174127748a8SRichard Marian Thomaiyar return outPayload; 175127748a8SRichard Marian Thomaiyar } 176127748a8SRichard Marian Thomaiyar 177127748a8SRichard Marian Thomaiyar // Perform user name based lookup 178127748a8SRichard Marian Thomaiyar std::string userName(request->user_name, request->user_name_len); 17905c1447dSJayaprakash Mutyala ipmi::SecureString passwd; 180127748a8SRichard Marian Thomaiyar uint8_t userId = ipmi::ipmiUserGetUserId(userName); 181127748a8SRichard Marian Thomaiyar if (userId == ipmi::invalidUserId) 182127748a8SRichard Marian Thomaiyar { 183127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 184127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 1850e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 186127748a8SRichard Marian Thomaiyar return outPayload; 187127748a8SRichard Marian Thomaiyar } 188127748a8SRichard Marian Thomaiyar // check user is enabled before proceeding. 189127748a8SRichard Marian Thomaiyar bool userEnabled = false; 190127748a8SRichard Marian Thomaiyar ipmi::ipmiUserCheckEnabled(userId, userEnabled); 191127748a8SRichard Marian Thomaiyar if (!userEnabled) 192127748a8SRichard Marian Thomaiyar { 193127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 194127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 1950e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 196127748a8SRichard Marian Thomaiyar return outPayload; 197127748a8SRichard Marian Thomaiyar } 1984c4694efSRichard Marian Thomaiyar // Get the user password for RAKP message authenticate 1994c4694efSRichard Marian Thomaiyar passwd = ipmi::ipmiUserGetPassword(userName); 2004c4694efSRichard Marian Thomaiyar if (passwd.empty()) 2014c4694efSRichard Marian Thomaiyar { 2024c4694efSRichard Marian Thomaiyar response->rmcpStatusCode = 2034c4694efSRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 2040e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 2054c4694efSRichard Marian Thomaiyar return outPayload; 2064c4694efSRichard Marian Thomaiyar } 207b31e9695SAyushi Smriti // Check whether user is already locked for failed attempts 208b31e9695SAyushi Smriti if (!ipmi::ipmiUserPamAuthenticate(userName, passwd)) 209b31e9695SAyushi Smriti { 210*7b7f25f7SGeorge Liu lg2::error( 211*7b7f25f7SGeorge Liu "Authentication failed - user already locked out, user id: {ID}", 212*7b7f25f7SGeorge Liu "ID", userId); 213b31e9695SAyushi Smriti 214b31e9695SAyushi Smriti response->rmcpStatusCode = 215b31e9695SAyushi Smriti static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 2160e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 217b31e9695SAyushi Smriti return outPayload; 218b31e9695SAyushi Smriti } 219d9c86bb7SSaravanan Palanisamy 220d9c86bb7SSaravanan Palanisamy uint8_t chNum = static_cast<uint8_t>(getInterfaceIndex()); 221127748a8SRichard Marian Thomaiyar // Get channel based access information 222992e53c7SRichard Marian Thomaiyar if ((ipmi::ipmiUserGetPrivilegeAccess( 223992e53c7SRichard Marian Thomaiyar userId, chNum, session->sessionUserPrivAccess) != IPMI_CC_OK) || 224992e53c7SRichard Marian Thomaiyar (ipmi::getChannelAccessData(chNum, session->sessionChannelAccess) != 225992e53c7SRichard Marian Thomaiyar IPMI_CC_OK)) 226127748a8SRichard Marian Thomaiyar { 227127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 228127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 2290e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 230127748a8SRichard Marian Thomaiyar return outPayload; 231127748a8SRichard Marian Thomaiyar } 232ecb32fbcSAppaRao Puli if (!isChannelAccessModeEnabled(session->sessionChannelAccess.accessMode)) 233ecb32fbcSAppaRao Puli { 234*7b7f25f7SGeorge Liu lg2::error("Channel access mode disabled."); 235ecb32fbcSAppaRao Puli response->rmcpStatusCode = 236ecb32fbcSAppaRao Puli static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 2370e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 238ecb32fbcSAppaRao Puli return outPayload; 239ecb32fbcSAppaRao Puli } 240992e53c7SRichard Marian Thomaiyar if (session->sessionUserPrivAccess.privilege > 241992e53c7SRichard Marian Thomaiyar static_cast<uint8_t>(session::Privilege::OEM)) 2427e5d38d2SRichard Marian Thomaiyar { 2437e5d38d2SRichard Marian Thomaiyar response->rmcpStatusCode = 2447e5d38d2SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 2450e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 2467e5d38d2SRichard Marian Thomaiyar return outPayload; 2477e5d38d2SRichard Marian Thomaiyar } 248f8a34fc4SSuryakanth Sekar session->channelNum(chNum); 249f8a34fc4SSuryakanth Sekar session->userID(userId); 2502555e2ecSjayaprakash Mutyala // minimum privilege of Channel / User / session::privilege::USER 251d5a4f45eSRichard Marian Thomaiyar // has to be used as session current privilege level 252127748a8SRichard Marian Thomaiyar uint8_t minPriv = 0; 253992e53c7SRichard Marian Thomaiyar if (session->sessionChannelAccess.privLimit < 254992e53c7SRichard Marian Thomaiyar session->sessionUserPrivAccess.privilege) 255127748a8SRichard Marian Thomaiyar { 256992e53c7SRichard Marian Thomaiyar minPriv = session->sessionChannelAccess.privLimit; 257127748a8SRichard Marian Thomaiyar } 258127748a8SRichard Marian Thomaiyar else 259127748a8SRichard Marian Thomaiyar { 260992e53c7SRichard Marian Thomaiyar minPriv = session->sessionUserPrivAccess.privilege; 261127748a8SRichard Marian Thomaiyar } 262f8a34fc4SSuryakanth Sekar if (session->currentPrivilege() > minPriv) 263127748a8SRichard Marian Thomaiyar { 264f8a34fc4SSuryakanth Sekar session->currentPrivilege(static_cast<uint8_t>(minPriv)); 265127748a8SRichard Marian Thomaiyar } 266d91fd9d2SRichard Marian Thomaiyar // For username / privilege lookup, fail with UNAUTH_NAME, if requested 267d8e92fe1SRichard Marian Thomaiyar // max privilege does not match user privilege 268d91fd9d2SRichard Marian Thomaiyar if (((request->req_max_privilege_level & userNameOnlyLookupMask) == 269d91fd9d2SRichard Marian Thomaiyar userNamePrivLookup) && 270d8e92fe1SRichard Marian Thomaiyar ((request->req_max_privilege_level & session::reqMaxPrivMask) != 271992e53c7SRichard Marian Thomaiyar session->sessionUserPrivAccess.privilege)) 272d91fd9d2SRichard Marian Thomaiyar { 273*7b7f25f7SGeorge Liu lg2::info("Username/Privilege lookup failed for requested privilege"); 274d91fd9d2SRichard Marian Thomaiyar response->rmcpStatusCode = 275d91fd9d2SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 2760e0546f1Ssunitakx 2770e0546f1Ssunitakx logInvalidLoginRedfishEvent(message); 278d91fd9d2SRichard Marian Thomaiyar return outPayload; 279d91fd9d2SRichard Marian Thomaiyar } 280127748a8SRichard Marian Thomaiyar 281127748a8SRichard Marian Thomaiyar std::fill(authAlgo->userKey.data(), 282127748a8SRichard Marian Thomaiyar authAlgo->userKey.data() + authAlgo->userKey.size(), 0); 283127748a8SRichard Marian Thomaiyar std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data()); 28499b87849SRichard Marian Thomaiyar 2858bb10b79STom Joseph // Copy the Managed System Random Number to the Authentication Algorithm 2868bb10b79STom Joseph std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN, 2878bb10b79STom Joseph authAlgo->bmcRandomNum.begin()); 2888bb10b79STom Joseph std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN); 2898bb10b79STom Joseph 2908bb10b79STom Joseph // Managed System GUID 29183029cb8STom Joseph std::copy_n(cache::guid.data(), cache::guid.size(), iter); 2928bb10b79STom Joseph std::advance(iter, BMC_GUID_LEN); 2938bb10b79STom Joseph 2948bb10b79STom Joseph // Requested Privilege Level 2958bb10b79STom Joseph std::copy_n(&(request->req_max_privilege_level), 2968bb10b79STom Joseph sizeof(request->req_max_privilege_level), iter); 2978bb10b79STom Joseph std::advance(iter, sizeof(request->req_max_privilege_level)); 2988bb10b79STom Joseph 2998bb10b79STom Joseph // User Name Length Byte 3008bb10b79STom Joseph std::copy_n(&(request->user_name_len), sizeof(request->user_name_len), 3018bb10b79STom Joseph iter); 30256527b93STom Joseph std::advance(iter, sizeof(request->user_name_len)); 30356527b93STom Joseph 30456527b93STom Joseph std::copy_n(session->userName.data(), session->userName.size(), iter); 3058bb10b79STom Joseph 3068bb10b79STom Joseph // Generate Key Exchange Authentication Code - RAKP2 3078bb10b79STom Joseph auto output = authAlgo->generateHMAC(input); 3088bb10b79STom Joseph 3098bb10b79STom Joseph response->messageTag = request->messageTag; 3108bb10b79STom Joseph response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR); 3118bb10b79STom Joseph response->reserved = 0; 3128bb10b79STom Joseph response->remoteConsoleSessionID = rcSessionID; 3138bb10b79STom Joseph 3148bb10b79STom Joseph // Copy Managed System Random Number to the Response 3158bb10b79STom Joseph std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(), 3168bb10b79STom Joseph response->managed_system_random_number); 3178bb10b79STom Joseph 3188bb10b79STom Joseph // Copy System GUID to the Response 3199e801a2bSVernon Mauery std::copy_n(cache::guid.data(), cache::guid.size(), 32083029cb8STom Joseph response->managed_system_guid); 3218bb10b79STom Joseph 3228bb10b79STom Joseph // Insert the HMAC output into the payload 3238bb10b79STom Joseph outPayload.insert(outPayload.end(), output.begin(), output.end()); 3248bb10b79STom Joseph return outPayload; 3258bb10b79STom Joseph } 3268bb10b79STom Joseph 3278bb10b79STom Joseph } // namespace command 328