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> 13fc37e59eSVernon Mauery #include <phosphor-logging/log.hpp> 148bb10b79STom Joseph 15fc37e59eSVernon Mauery using namespace phosphor::logging; 16fc37e59eSVernon Mauery 178bb10b79STom Joseph namespace command 188bb10b79STom Joseph { 198bb10b79STom Joseph 2018a45e9dSTom Joseph std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload, 218bb10b79STom Joseph const message::Handler& handler) 228bb10b79STom Joseph { 238bb10b79STom Joseph std::vector<uint8_t> outPayload(sizeof(RAKP2response)); 2418a45e9dSTom Joseph auto request = reinterpret_cast<const RAKP1request*>(inPayload.data()); 258bb10b79STom Joseph auto response = reinterpret_cast<RAKP2response*>(outPayload.data()); 268bb10b79STom Joseph 278bb10b79STom Joseph // Session ID zero is reserved for Session Setup 288bb10b79STom Joseph if (endian::from_ipmi(request->managedSystemSessionID) == 29*f8a34fc4SSuryakanth Sekar session::sessionZero) 308bb10b79STom Joseph { 31fc37e59eSVernon Mauery log<level::INFO>("RAKP12: BMC invalid Session ID"); 328bb10b79STom Joseph response->rmcpStatusCode = 338bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 348bb10b79STom Joseph return outPayload; 358bb10b79STom Joseph } 368bb10b79STom Joseph 378bb10b79STom Joseph std::shared_ptr<session::Session> session; 388bb10b79STom Joseph try 398bb10b79STom Joseph { 40ae1fda44SVernon Mauery session = 41ae1fda44SVernon Mauery std::get<session::Manager&>(singletonPool) 42ae1fda44SVernon Mauery .getSession(endian::from_ipmi(request->managedSystemSessionID)); 438bb10b79STom Joseph } 448bb10b79STom Joseph catch (std::exception& e) 458bb10b79STom Joseph { 46fc37e59eSVernon Mauery log<level::ERR>("RAKP12 : session not found", 47fc37e59eSVernon Mauery entry("EXCEPTION=%s", e.what())); 488bb10b79STom Joseph response->rmcpStatusCode = 498bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID); 508bb10b79STom Joseph return outPayload; 518bb10b79STom Joseph } 528bb10b79STom Joseph 539e801a2bSVernon Mauery auto rakp1Size = 549e801a2bSVernon Mauery sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len); 5556527b93STom Joseph 5656527b93STom Joseph // Validate user name length in the message 5756527b93STom Joseph if (request->user_name_len > userNameMaxLen || 5856527b93STom Joseph inPayload.size() != rakp1Size) 5956527b93STom Joseph { 6056527b93STom Joseph response->rmcpStatusCode = 6156527b93STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH); 6256527b93STom Joseph return outPayload; 6356527b93STom Joseph } 6456527b93STom Joseph 6556527b93STom Joseph session->userName.assign(request->user_name, request->user_name_len); 6656527b93STom Joseph 678bb10b79STom Joseph // Update transaction time 688bb10b79STom Joseph session->updateLastTransactionTime(); 698bb10b79STom Joseph 708bb10b79STom Joseph auto rcSessionID = endian::to_ipmi(session->getRCSessionID()); 718bb10b79STom Joseph auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID()); 728bb10b79STom Joseph auto authAlgo = session->getAuthAlgo(); 738bb10b79STom Joseph 748bb10b79STom Joseph /* 758bb10b79STom Joseph * Generate Key Authentication Code - RAKP 2 768bb10b79STom Joseph * 778bb10b79STom Joseph * 1) Remote Console Session ID - 4 bytes 788bb10b79STom Joseph * 2) Managed System Session ID - 4 bytes 798bb10b79STom Joseph * 3) Remote Console Random Number - 16 bytes 808bb10b79STom Joseph * 4) Managed System Random Number - 16 bytes 818bb10b79STom Joseph * 5) Managed System GUID - 16 bytes 828bb10b79STom Joseph * 6) Requested Privilege Level - 1 byte 838bb10b79STom Joseph * 7) User Name Length Byte - 1 byte (0 for 'null' username) 848bb10b79STom Joseph * 8) User Name - variable (absent for 'null' username) 858bb10b79STom Joseph */ 868bb10b79STom Joseph 878bb10b79STom Joseph std::vector<uint8_t> input; 888bb10b79STom Joseph input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) + 898bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN + 909e801a2bSVernon Mauery cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN + 919e801a2bSVernon Mauery sizeof(request->req_max_privilege_level) + 929e801a2bSVernon Mauery sizeof(request->user_name_len) + session->userName.size()); 938bb10b79STom Joseph 948bb10b79STom Joseph auto iter = input.begin(); 958bb10b79STom Joseph 968bb10b79STom Joseph // Remote Console Session ID 979e801a2bSVernon Mauery std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID), 989e801a2bSVernon Mauery iter); 998bb10b79STom Joseph std::advance(iter, sizeof(rcSessionID)); 1008bb10b79STom Joseph 1018bb10b79STom Joseph // Managed System Session ID 1028bb10b79STom Joseph std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID), 1038bb10b79STom Joseph iter); 1048bb10b79STom Joseph std::advance(iter, sizeof(bmcSessionID)); 1058bb10b79STom Joseph 1068bb10b79STom Joseph // Copy the Remote Console Random Number from the RAKP1 request to the 1078bb10b79STom Joseph // Authentication Algorithm 1089e801a2bSVernon Mauery std::copy_n( 1099e801a2bSVernon Mauery reinterpret_cast<const uint8_t*>(request->remote_console_random_number), 1108bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 1118bb10b79STom Joseph authAlgo->rcRandomNum.begin()); 1128bb10b79STom Joseph 1139e801a2bSVernon Mauery std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter); 1148bb10b79STom Joseph std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN); 1158bb10b79STom Joseph 1168bb10b79STom Joseph // Generate the Managed System Random Number 1178bb10b79STom Joseph if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) + 1188bb10b79STom Joseph cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN, 1198bb10b79STom Joseph cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN)) 1208bb10b79STom Joseph { 1218bb10b79STom Joseph response->rmcpStatusCode = 1228bb10b79STom Joseph static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE); 1238bb10b79STom Joseph return outPayload; 1248bb10b79STom Joseph } 125d5a4f45eSRichard Marian Thomaiyar // As stated in Set Session Privilege Level command in IPMI Spec, when 126d5a4f45eSRichard Marian Thomaiyar // creating a session through Activate command / RAKP 1 message, it must be 127d5a4f45eSRichard Marian Thomaiyar // established with CALLBACK privilege if requested for callback. All other 128d5a4f45eSRichard Marian Thomaiyar // sessions are initialy set to USER privilege, regardless of the requested 129d5a4f45eSRichard Marian Thomaiyar // maximum privilege. 130*f8a34fc4SSuryakanth Sekar session->currentPrivilege( 131*f8a34fc4SSuryakanth Sekar static_cast<uint8_t>(session::Privilege::CALLBACK)); 132d5a4f45eSRichard Marian Thomaiyar if (static_cast<session::Privilege>(request->req_max_privilege_level & 133d5a4f45eSRichard Marian Thomaiyar session::reqMaxPrivMask) > 134d5a4f45eSRichard Marian Thomaiyar session::Privilege::CALLBACK) 135d5a4f45eSRichard Marian Thomaiyar { 136*f8a34fc4SSuryakanth Sekar session->currentPrivilege( 137*f8a34fc4SSuryakanth Sekar static_cast<uint8_t>(session::Privilege::USER)); 138d5a4f45eSRichard Marian Thomaiyar } 1394021b1f7STom Joseph session->reqMaxPrivLevel = 1404021b1f7STom Joseph static_cast<session::Privilege>(request->req_max_privilege_level); 141d91fd9d2SRichard Marian Thomaiyar if (request->user_name_len == 0) 142127748a8SRichard Marian Thomaiyar { 143d91fd9d2SRichard Marian Thomaiyar // Bail out, if user name is not specified. 144d91fd9d2SRichard Marian Thomaiyar // Yes, NULL user name is not supported for security reasons. 145127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 146127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 147127748a8SRichard Marian Thomaiyar return outPayload; 148127748a8SRichard Marian Thomaiyar } 149127748a8SRichard Marian Thomaiyar 150127748a8SRichard Marian Thomaiyar // Perform user name based lookup 151127748a8SRichard Marian Thomaiyar std::string userName(request->user_name, request->user_name_len); 152127748a8SRichard Marian Thomaiyar std::string passwd; 153127748a8SRichard Marian Thomaiyar uint8_t userId = ipmi::ipmiUserGetUserId(userName); 154127748a8SRichard Marian Thomaiyar if (userId == ipmi::invalidUserId) 155127748a8SRichard Marian Thomaiyar { 156127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 157127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 158127748a8SRichard Marian Thomaiyar return outPayload; 159127748a8SRichard Marian Thomaiyar } 160127748a8SRichard Marian Thomaiyar // check user is enabled before proceeding. 161127748a8SRichard Marian Thomaiyar bool userEnabled = false; 162127748a8SRichard Marian Thomaiyar ipmi::ipmiUserCheckEnabled(userId, userEnabled); 163127748a8SRichard Marian Thomaiyar if (!userEnabled) 164127748a8SRichard Marian Thomaiyar { 165127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 166127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 167127748a8SRichard Marian Thomaiyar return outPayload; 168127748a8SRichard Marian Thomaiyar } 1694c4694efSRichard Marian Thomaiyar // Get the user password for RAKP message authenticate 1704c4694efSRichard Marian Thomaiyar passwd = ipmi::ipmiUserGetPassword(userName); 1714c4694efSRichard Marian Thomaiyar if (passwd.empty()) 1724c4694efSRichard Marian Thomaiyar { 1734c4694efSRichard Marian Thomaiyar response->rmcpStatusCode = 1744c4694efSRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 1754c4694efSRichard Marian Thomaiyar return outPayload; 1764c4694efSRichard Marian Thomaiyar } 177b31e9695SAyushi Smriti // Check whether user is already locked for failed attempts 178b31e9695SAyushi Smriti if (!ipmi::ipmiUserPamAuthenticate(userName, passwd)) 179b31e9695SAyushi Smriti { 180b31e9695SAyushi Smriti log<level::ERR>("Authentication failed - user already locked out", 181b31e9695SAyushi Smriti entry("USER-ID=%d", static_cast<uint8_t>(userId))); 182b31e9695SAyushi Smriti 183b31e9695SAyushi Smriti response->rmcpStatusCode = 184b31e9695SAyushi Smriti static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 185b31e9695SAyushi Smriti return outPayload; 186b31e9695SAyushi Smriti } 187127748a8SRichard Marian Thomaiyar // TODO Replace with proper calls. 188127748a8SRichard Marian Thomaiyar uint8_t chNum = static_cast<uint8_t>(ipmi::EChannelID::chanLan1); 189127748a8SRichard Marian Thomaiyar // Get channel based access information 190992e53c7SRichard Marian Thomaiyar if ((ipmi::ipmiUserGetPrivilegeAccess( 191992e53c7SRichard Marian Thomaiyar userId, chNum, session->sessionUserPrivAccess) != IPMI_CC_OK) || 192992e53c7SRichard Marian Thomaiyar (ipmi::getChannelAccessData(chNum, session->sessionChannelAccess) != 193992e53c7SRichard Marian Thomaiyar IPMI_CC_OK)) 194127748a8SRichard Marian Thomaiyar { 195127748a8SRichard Marian Thomaiyar response->rmcpStatusCode = 196127748a8SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 197127748a8SRichard Marian Thomaiyar return outPayload; 198127748a8SRichard Marian Thomaiyar } 199992e53c7SRichard Marian Thomaiyar if (session->sessionUserPrivAccess.privilege > 200992e53c7SRichard Marian Thomaiyar static_cast<uint8_t>(session::Privilege::OEM)) 2017e5d38d2SRichard Marian Thomaiyar { 2027e5d38d2SRichard Marian Thomaiyar response->rmcpStatusCode = 2037e5d38d2SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE); 2047e5d38d2SRichard Marian Thomaiyar return outPayload; 2057e5d38d2SRichard Marian Thomaiyar } 206*f8a34fc4SSuryakanth Sekar session->channelNum(chNum); 207*f8a34fc4SSuryakanth Sekar session->userID(userId); 208d5a4f45eSRichard Marian Thomaiyar // minimum privilege of Channel / User / session::privilege::USER/CALLBACK / 209d5a4f45eSRichard Marian Thomaiyar // has to be used as session current privilege level 210127748a8SRichard Marian Thomaiyar uint8_t minPriv = 0; 211992e53c7SRichard Marian Thomaiyar if (session->sessionChannelAccess.privLimit < 212992e53c7SRichard Marian Thomaiyar session->sessionUserPrivAccess.privilege) 213127748a8SRichard Marian Thomaiyar { 214992e53c7SRichard Marian Thomaiyar minPriv = session->sessionChannelAccess.privLimit; 215127748a8SRichard Marian Thomaiyar } 216127748a8SRichard Marian Thomaiyar else 217127748a8SRichard Marian Thomaiyar { 218992e53c7SRichard Marian Thomaiyar minPriv = session->sessionUserPrivAccess.privilege; 219127748a8SRichard Marian Thomaiyar } 220*f8a34fc4SSuryakanth Sekar if (session->currentPrivilege() > minPriv) 221127748a8SRichard Marian Thomaiyar { 222*f8a34fc4SSuryakanth Sekar session->currentPrivilege(static_cast<uint8_t>(minPriv)); 223127748a8SRichard Marian Thomaiyar } 224d91fd9d2SRichard Marian Thomaiyar // For username / privilege lookup, fail with UNAUTH_NAME, if requested 225d8e92fe1SRichard Marian Thomaiyar // max privilege does not match user privilege 226d91fd9d2SRichard Marian Thomaiyar if (((request->req_max_privilege_level & userNameOnlyLookupMask) == 227d91fd9d2SRichard Marian Thomaiyar userNamePrivLookup) && 228d8e92fe1SRichard Marian Thomaiyar ((request->req_max_privilege_level & session::reqMaxPrivMask) != 229992e53c7SRichard Marian Thomaiyar session->sessionUserPrivAccess.privilege)) 230d91fd9d2SRichard Marian Thomaiyar { 231fc37e59eSVernon Mauery log<level::INFO>( 232fc37e59eSVernon Mauery "Username/Privilege lookup failed for requested privilege"); 233d91fd9d2SRichard Marian Thomaiyar response->rmcpStatusCode = 234d91fd9d2SRichard Marian Thomaiyar static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME); 235d91fd9d2SRichard Marian Thomaiyar return outPayload; 236d91fd9d2SRichard Marian Thomaiyar } 237127748a8SRichard Marian Thomaiyar 238127748a8SRichard Marian Thomaiyar std::fill(authAlgo->userKey.data(), 239127748a8SRichard Marian Thomaiyar authAlgo->userKey.data() + authAlgo->userKey.size(), 0); 240127748a8SRichard Marian Thomaiyar std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data()); 24199b87849SRichard Marian Thomaiyar 2428bb10b79STom Joseph // Copy the Managed System Random Number to the Authentication Algorithm 2438bb10b79STom Joseph std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN, 2448bb10b79STom Joseph authAlgo->bmcRandomNum.begin()); 2458bb10b79STom Joseph std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN); 2468bb10b79STom Joseph 2478bb10b79STom Joseph // Managed System GUID 24883029cb8STom Joseph std::copy_n(cache::guid.data(), cache::guid.size(), iter); 2498bb10b79STom Joseph std::advance(iter, BMC_GUID_LEN); 2508bb10b79STom Joseph 2518bb10b79STom Joseph // Requested Privilege Level 2528bb10b79STom Joseph std::copy_n(&(request->req_max_privilege_level), 2538bb10b79STom Joseph sizeof(request->req_max_privilege_level), iter); 2548bb10b79STom Joseph std::advance(iter, sizeof(request->req_max_privilege_level)); 2558bb10b79STom Joseph 2568bb10b79STom Joseph // User Name Length Byte 2578bb10b79STom Joseph std::copy_n(&(request->user_name_len), sizeof(request->user_name_len), 2588bb10b79STom Joseph iter); 25956527b93STom Joseph std::advance(iter, sizeof(request->user_name_len)); 26056527b93STom Joseph 26156527b93STom Joseph std::copy_n(session->userName.data(), session->userName.size(), iter); 2628bb10b79STom Joseph 2638bb10b79STom Joseph // Generate Key Exchange Authentication Code - RAKP2 2648bb10b79STom Joseph auto output = authAlgo->generateHMAC(input); 2658bb10b79STom Joseph 2668bb10b79STom Joseph response->messageTag = request->messageTag; 2678bb10b79STom Joseph response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR); 2688bb10b79STom Joseph response->reserved = 0; 2698bb10b79STom Joseph response->remoteConsoleSessionID = rcSessionID; 2708bb10b79STom Joseph 2718bb10b79STom Joseph // Copy Managed System Random Number to the Response 2728bb10b79STom Joseph std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(), 2738bb10b79STom Joseph response->managed_system_random_number); 2748bb10b79STom Joseph 2758bb10b79STom Joseph // Copy System GUID to the Response 2769e801a2bSVernon Mauery std::copy_n(cache::guid.data(), cache::guid.size(), 27783029cb8STom Joseph response->managed_system_guid); 2788bb10b79STom Joseph 2798bb10b79STom Joseph // Insert the HMAC output into the payload 2808bb10b79STom Joseph outPayload.insert(outPayload.end(), output.begin(), output.end()); 2818bb10b79STom Joseph return outPayload; 2828bb10b79STom Joseph } 2838bb10b79STom Joseph 2848bb10b79STom Joseph } // namespace command 285