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