blob: c72611e1e7d85a6ef46e60bc98a3850510d44889 [file] [log] [blame]
Tom Joseph8bb10b72016-12-06 17:47:56 +05301#include "rakp12.hpp"
2
Vernon Mauery9e801a22018-10-12 13:20:49 -07003#include "comm_module.hpp"
4#include "endian.hpp"
5#include "guid.hpp"
Vernon Mauery2085ae02021-06-10 11:51:00 -07006#include "sessions_manager.hpp"
Vernon Mauery9e801a22018-10-12 13:20:49 -07007
Tom Joseph8bb10b72016-12-06 17:47:56 +05308#include <openssl/rand.h>
9
George Liubc8958f2022-07-04 09:29:49 +080010#include <ipmid/types.hpp>
George Liu7b7f25f2022-07-04 17:07:32 +080011#include <phosphor-logging/lg2.hpp>
George Liubc8958f2022-07-04 09:29:49 +080012
Tom Joseph8bb10b72016-12-06 17:47:56 +053013#include <algorithm>
Tom Joseph56527b92018-03-21 19:31:58 +053014#include <cstring>
Tom Joseph8bb10b72016-12-06 17:47:56 +053015#include <iomanip>
Tom Joseph8bb10b72016-12-06 17:47:56 +053016
Tom Joseph8bb10b72016-12-06 17:47:56 +053017namespace command
18{
19
AppaRao Puliecb32fb2020-07-01 22:55:38 +053020bool isChannelAccessModeEnabled(const uint8_t accessMode)
21{
22 return accessMode !=
23 static_cast<uint8_t>(ipmi::EChannelAccessMode::disabled);
24}
25
sunitakx0e0546f2021-06-15 08:57:10 +000026void logInvalidLoginRedfishEvent(const std::string& journalMsg,
27 const std::optional<std::string>& messageArgs)
28{
29 static constexpr std::string_view openBMCMessageRegistryVersion = "0.1.";
30 std::string messageID = "OpenBMC." +
31 std::string(openBMCMessageRegistryVersion) +
32 "InvalidLoginAttempted";
George Liu7b7f25f2022-07-04 17:07:32 +080033 lg2::error(
34 "message: {MSG}, id: {REDFISH_MESSAGE_ID}, args: {REDFISH_MESSAGE_ARGS}",
35 "MSG", journalMsg, "REDFISH_MESSAGE_ID", messageID,
36 "REDFISH_MESSAGE_ARGS", messageArgs.value());
sunitakx0e0546f2021-06-15 08:57:10 +000037}
Tom Joseph18a45e92017-04-11 11:30:44 +053038std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload,
George Liube1470c2022-07-04 14:33:24 +080039 std::shared_ptr<message::Handler>& /* handler */)
Tom Joseph8bb10b72016-12-06 17:47:56 +053040{
Tom Joseph18a45e92017-04-11 11:30:44 +053041 auto request = reinterpret_cast<const RAKP1request*>(inPayload.data());
Zhikui Ren2b1edef2020-07-24 14:32:13 -070042 // verify inPayload minimum size
43 if (inPayload.size() < (sizeof(*request) - userNameMaxLen))
44 {
45 std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
46 return errorPayload;
47 }
48
49 std::vector<uint8_t> outPayload(sizeof(RAKP2response));
Tom Joseph8bb10b72016-12-06 17:47:56 +053050 auto response = reinterpret_cast<RAKP2response*>(outPayload.data());
51
52 // Session ID zero is reserved for Session Setup
Vernon Mauery9e801a22018-10-12 13:20:49 -070053 if (endian::from_ipmi(request->managedSystemSessionID) ==
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053054 session::sessionZero)
Tom Joseph8bb10b72016-12-06 17:47:56 +053055 {
George Liu7b7f25f2022-07-04 17:07:32 +080056 lg2::info("RAKP12: BMC invalid Session ID");
Tom Joseph8bb10b72016-12-06 17:47:56 +053057 response->rmcpStatusCode =
58 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
59 return outPayload;
60 }
61
62 std::shared_ptr<session::Session> session;
63 try
64 {
Vernon Mauery2085ae02021-06-10 11:51:00 -070065 session = session::Manager::get().getSession(
66 endian::from_ipmi(request->managedSystemSessionID));
Tom Joseph8bb10b72016-12-06 17:47:56 +053067 }
Patrick Williams12d199b2021-10-06 12:36:48 -050068 catch (const std::exception& e)
Tom Joseph8bb10b72016-12-06 17:47:56 +053069 {
George Liu7b7f25f2022-07-04 17:07:32 +080070 lg2::error("RAKP12 : session not found: {ERROR}", "ERROR", e);
Tom Joseph8bb10b72016-12-06 17:47:56 +053071 response->rmcpStatusCode =
72 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
73 return outPayload;
74 }
75
Vernon Mauery9e801a22018-10-12 13:20:49 -070076 auto rakp1Size =
77 sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len);
Tom Joseph56527b92018-03-21 19:31:58 +053078
sunitakx0e0546f2021-06-15 08:57:10 +000079 std::string message = "Invalid login attempted via RCMPP interface ";
Tom Joseph56527b92018-03-21 19:31:58 +053080 // Validate user name length in the message
81 if (request->user_name_len > userNameMaxLen ||
Vernon Mauery9e801a22018-10-12 13:20:49 -070082 inPayload.size() != rakp1Size)
Tom Joseph56527b92018-03-21 19:31:58 +053083 {
84 response->rmcpStatusCode =
85 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH);
sunitakx0e0546f2021-06-15 08:57:10 +000086 logInvalidLoginRedfishEvent(message);
Tom Joseph56527b92018-03-21 19:31:58 +053087 return outPayload;
88 }
89
90 session->userName.assign(request->user_name, request->user_name_len);
91
Tom Joseph8bb10b72016-12-06 17:47:56 +053092 // Update transaction time
93 session->updateLastTransactionTime();
94
95 auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
96 auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
97 auto authAlgo = session->getAuthAlgo();
98
99 /*
100 * Generate Key Authentication Code - RAKP 2
101 *
102 * 1) Remote Console Session ID - 4 bytes
103 * 2) Managed System Session ID - 4 bytes
104 * 3) Remote Console Random Number - 16 bytes
105 * 4) Managed System Random Number - 16 bytes
106 * 5) Managed System GUID - 16 bytes
107 * 6) Requested Privilege Level - 1 byte
108 * 7) User Name Length Byte - 1 byte (0 for 'null' username)
109 * 8) User Name - variable (absent for 'null' username)
110 */
111
112 std::vector<uint8_t> input;
113 input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) +
114 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700115 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN +
116 sizeof(request->req_max_privilege_level) +
117 sizeof(request->user_name_len) + session->userName.size());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530118
119 auto iter = input.begin();
120
121 // Remote Console Session ID
Vernon Mauery9e801a22018-10-12 13:20:49 -0700122 std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
123 iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530124 std::advance(iter, sizeof(rcSessionID));
125
126 // Managed System Session ID
127 std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
128 iter);
129 std::advance(iter, sizeof(bmcSessionID));
130
131 // Copy the Remote Console Random Number from the RAKP1 request to the
132 // Authentication Algorithm
Vernon Mauery9e801a22018-10-12 13:20:49 -0700133 std::copy_n(
134 reinterpret_cast<const uint8_t*>(request->remote_console_random_number),
135 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
136 authAlgo->rcRandomNum.begin());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530137
Vernon Mauery9e801a22018-10-12 13:20:49 -0700138 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530139 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
140
141 // Generate the Managed System Random Number
142 if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700143 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
Tom Joseph8bb10b72016-12-06 17:47:56 +0530144 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN))
145 {
146 response->rmcpStatusCode =
147 static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE);
148 return outPayload;
149 }
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530150 // As stated in Set Session Privilege Level command in IPMI Spec, when
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000151 // creating a session through Activate command / RAKP 1 message, it must
152 // be established with USER privilege as well as all other sessions are
153 // initially set to USER privilege, regardless of the requested maximum
154 // privilege.
155 if (!(static_cast<session::Privilege>(request->req_max_privilege_level &
156 session::reqMaxPrivMask) >
157 session::Privilege::CALLBACK))
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530158 {
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000159 response->rmcpStatusCode =
160 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_ROLE_PRIV);
161 return outPayload;
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530162 }
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000163 session->currentPrivilege(static_cast<uint8_t>(session::Privilege::USER));
164
Tom Joseph4021b1f2019-02-12 10:10:12 +0530165 session->reqMaxPrivLevel =
166 static_cast<session::Privilege>(request->req_max_privilege_level);
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530167 if (request->user_name_len == 0)
Richard Marian Thomaiyar127748a2018-09-06 07:08:51 +0530168 {
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530169 // Bail out, if user name is not specified.
170 // Yes, NULL user name is not supported for security reasons.
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530171 response->rmcpStatusCode =
172 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
sunitakx0e0546f2021-06-15 08:57:10 +0000173 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530174 return outPayload;
Richard Marian Thomaiyard2563c52018-11-29 11:49:10 +0530175 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530176
177 // Perform user name based lookup
178 std::string userName(request->user_name, request->user_name_len);
Jayaprakash Mutyala05c14472020-10-19 10:26:24 +0000179 ipmi::SecureString passwd;
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530180 uint8_t userId = ipmi::ipmiUserGetUserId(userName);
181 if (userId == ipmi::invalidUserId)
182 {
183 response->rmcpStatusCode =
184 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
sunitakx0e0546f2021-06-15 08:57:10 +0000185 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530186 return outPayload;
187 }
188 // check user is enabled before proceeding.
189 bool userEnabled = false;
190 ipmi::ipmiUserCheckEnabled(userId, userEnabled);
191 if (!userEnabled)
192 {
193 response->rmcpStatusCode =
194 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
sunitakx0e0546f2021-06-15 08:57:10 +0000195 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530196 return outPayload;
197 }
Richard Marian Thomaiyar4c4694e2019-06-20 16:36:49 +0530198 // Get the user password for RAKP message authenticate
199 passwd = ipmi::ipmiUserGetPassword(userName);
200 if (passwd.empty())
201 {
202 response->rmcpStatusCode =
203 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
sunitakx0e0546f2021-06-15 08:57:10 +0000204 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar4c4694e2019-06-20 16:36:49 +0530205 return outPayload;
206 }
Ayushi Smritib31e9692019-05-15 12:06:51 +0000207 // Check whether user is already locked for failed attempts
208 if (!ipmi::ipmiUserPamAuthenticate(userName, passwd))
209 {
George Liu7b7f25f2022-07-04 17:07:32 +0800210 lg2::error(
211 "Authentication failed - user already locked out, user id: {ID}",
212 "ID", userId);
Ayushi Smritib31e9692019-05-15 12:06:51 +0000213
214 response->rmcpStatusCode =
215 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
sunitakx0e0546f2021-06-15 08:57:10 +0000216 logInvalidLoginRedfishEvent(message);
Ayushi Smritib31e9692019-05-15 12:06:51 +0000217 return outPayload;
218 }
Saravanan Palanisamyd9c86bb2019-08-07 17:57:37 +0000219
220 uint8_t chNum = static_cast<uint8_t>(getInterfaceIndex());
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530221 // Get channel based access information
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530222 if ((ipmi::ipmiUserGetPrivilegeAccess(
223 userId, chNum, session->sessionUserPrivAccess) != IPMI_CC_OK) ||
224 (ipmi::getChannelAccessData(chNum, session->sessionChannelAccess) !=
225 IPMI_CC_OK))
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530226 {
227 response->rmcpStatusCode =
228 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
sunitakx0e0546f2021-06-15 08:57:10 +0000229 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530230 return outPayload;
231 }
AppaRao Puliecb32fb2020-07-01 22:55:38 +0530232 if (!isChannelAccessModeEnabled(session->sessionChannelAccess.accessMode))
233 {
George Liu7b7f25f2022-07-04 17:07:32 +0800234 lg2::error("Channel access mode disabled.");
AppaRao Puliecb32fb2020-07-01 22:55:38 +0530235 response->rmcpStatusCode =
236 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
sunitakx0e0546f2021-06-15 08:57:10 +0000237 logInvalidLoginRedfishEvent(message);
AppaRao Puliecb32fb2020-07-01 22:55:38 +0530238 return outPayload;
239 }
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530240 if (session->sessionUserPrivAccess.privilege >
241 static_cast<uint8_t>(session::Privilege::OEM))
Richard Marian Thomaiyar7e5d38d2019-03-02 22:11:41 +0530242 {
243 response->rmcpStatusCode =
244 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
sunitakx0e0546f2021-06-15 08:57:10 +0000245 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar7e5d38d2019-03-02 22:11:41 +0530246 return outPayload;
247 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530248 session->channelNum(chNum);
249 session->userID(userId);
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000250 // minimum privilege of Channel / User / session::privilege::USER
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530251 // has to be used as session current privilege level
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530252 uint8_t minPriv = 0;
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530253 if (session->sessionChannelAccess.privLimit <
254 session->sessionUserPrivAccess.privilege)
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530255 {
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530256 minPriv = session->sessionChannelAccess.privLimit;
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530257 }
258 else
259 {
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530260 minPriv = session->sessionUserPrivAccess.privilege;
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530261 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530262 if (session->currentPrivilege() > minPriv)
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530263 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530264 session->currentPrivilege(static_cast<uint8_t>(minPriv));
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530265 }
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530266 // For username / privilege lookup, fail with UNAUTH_NAME, if requested
Richard Marian Thomaiyard8e92fe2019-01-16 11:56:23 +0530267 // max privilege does not match user privilege
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530268 if (((request->req_max_privilege_level & userNameOnlyLookupMask) ==
269 userNamePrivLookup) &&
Richard Marian Thomaiyard8e92fe2019-01-16 11:56:23 +0530270 ((request->req_max_privilege_level & session::reqMaxPrivMask) !=
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530271 session->sessionUserPrivAccess.privilege))
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530272 {
George Liu7b7f25f2022-07-04 17:07:32 +0800273 lg2::info("Username/Privilege lookup failed for requested privilege");
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530274 response->rmcpStatusCode =
275 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
sunitakx0e0546f2021-06-15 08:57:10 +0000276
277 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530278 return outPayload;
279 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530280
281 std::fill(authAlgo->userKey.data(),
282 authAlgo->userKey.data() + authAlgo->userKey.size(), 0);
283 std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data());
284
Tom Joseph8bb10b72016-12-06 17:47:56 +0530285 // Copy the Managed System Random Number to the Authentication Algorithm
286 std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN,
287 authAlgo->bmcRandomNum.begin());
288 std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
289
290 // Managed System GUID
Tom Joseph83029cb2017-09-01 16:37:31 +0530291 std::copy_n(cache::guid.data(), cache::guid.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530292 std::advance(iter, BMC_GUID_LEN);
293
294 // Requested Privilege Level
Tom Joseph8bb10b72016-12-06 17:47:56 +0530295 std::copy_n(&(request->req_max_privilege_level),
296 sizeof(request->req_max_privilege_level), iter);
297 std::advance(iter, sizeof(request->req_max_privilege_level));
298
Tom Joseph8bb10b72016-12-06 17:47:56 +0530299 // User Name Length Byte
300 std::copy_n(&(request->user_name_len), sizeof(request->user_name_len),
301 iter);
Tom Joseph56527b92018-03-21 19:31:58 +0530302 std::advance(iter, sizeof(request->user_name_len));
303
304 std::copy_n(session->userName.data(), session->userName.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530305
306 // Generate Key Exchange Authentication Code - RAKP2
307 auto output = authAlgo->generateHMAC(input);
308
309 response->messageTag = request->messageTag;
310 response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
311 response->reserved = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700312 response->remoteConsoleSessionID = rcSessionID;
Tom Joseph8bb10b72016-12-06 17:47:56 +0530313
314 // Copy Managed System Random Number to the Response
315 std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
316 response->managed_system_random_number);
317
318 // Copy System GUID to the Response
Vernon Mauery9e801a22018-10-12 13:20:49 -0700319 std::copy_n(cache::guid.data(), cache::guid.size(),
Tom Joseph83029cb2017-09-01 16:37:31 +0530320 response->managed_system_guid);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530321
322 // Insert the HMAC output into the payload
323 outPayload.insert(outPayload.end(), output.begin(), output.end());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530324 return outPayload;
325}
326
327} // namespace command