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