blob: bec5c4f04e448db8c743121539a5b01e8e552fd8 [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>
11#include <phosphor-logging/log.hpp>
12
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
Vernon Maueryfc37e592018-12-19 14:55:15 -080017using namespace phosphor::logging;
18
Tom Joseph8bb10b72016-12-06 17:47:56 +053019namespace command
20{
21
AppaRao Puliecb32fb2020-07-01 22:55:38 +053022bool isChannelAccessModeEnabled(const uint8_t accessMode)
23{
24 return accessMode !=
25 static_cast<uint8_t>(ipmi::EChannelAccessMode::disabled);
26}
27
sunitakx0e0546f2021-06-15 08:57:10 +000028void logInvalidLoginRedfishEvent(const std::string& journalMsg,
29 const std::optional<std::string>& messageArgs)
30{
31 static constexpr std::string_view openBMCMessageRegistryVersion = "0.1.";
32 std::string messageID = "OpenBMC." +
33 std::string(openBMCMessageRegistryVersion) +
34 "InvalidLoginAttempted";
35 phosphor::logging::log<phosphor::logging::level::ERR>(
36 journalMsg.c_str(),
37 phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageID.c_str()),
38 phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s",
39 messageArgs.value().c_str()));
40}
Tom Joseph18a45e92017-04-11 11:30:44 +053041std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload,
Vernon Mauery41ff9b52021-06-11 11:37:40 -070042 std::shared_ptr<message::Handler>& handler)
Tom Joseph8bb10b72016-12-06 17:47:56 +053043{
Tom Joseph18a45e92017-04-11 11:30:44 +053044 auto request = reinterpret_cast<const RAKP1request*>(inPayload.data());
Zhikui Ren2b1edef2020-07-24 14:32:13 -070045 // verify inPayload minimum size
46 if (inPayload.size() < (sizeof(*request) - userNameMaxLen))
47 {
48 std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
49 return errorPayload;
50 }
51
52 std::vector<uint8_t> outPayload(sizeof(RAKP2response));
Tom Joseph8bb10b72016-12-06 17:47:56 +053053 auto response = reinterpret_cast<RAKP2response*>(outPayload.data());
54
55 // Session ID zero is reserved for Session Setup
Vernon Mauery9e801a22018-10-12 13:20:49 -070056 if (endian::from_ipmi(request->managedSystemSessionID) ==
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053057 session::sessionZero)
Tom Joseph8bb10b72016-12-06 17:47:56 +053058 {
Vernon Maueryfc37e592018-12-19 14:55:15 -080059 log<level::INFO>("RAKP12: BMC invalid Session ID");
Tom Joseph8bb10b72016-12-06 17:47:56 +053060 response->rmcpStatusCode =
61 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
62 return outPayload;
63 }
64
65 std::shared_ptr<session::Session> session;
66 try
67 {
Vernon Mauery2085ae02021-06-10 11:51:00 -070068 session = session::Manager::get().getSession(
69 endian::from_ipmi(request->managedSystemSessionID));
Tom Joseph8bb10b72016-12-06 17:47:56 +053070 }
Patrick Williams12d199b2021-10-06 12:36:48 -050071 catch (const std::exception& e)
Tom Joseph8bb10b72016-12-06 17:47:56 +053072 {
Vernon Maueryfc37e592018-12-19 14:55:15 -080073 log<level::ERR>("RAKP12 : session not found",
74 entry("EXCEPTION=%s", e.what()));
Tom Joseph8bb10b72016-12-06 17:47:56 +053075 response->rmcpStatusCode =
76 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
77 return outPayload;
78 }
79
Vernon Mauery9e801a22018-10-12 13:20:49 -070080 auto rakp1Size =
81 sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len);
Tom Joseph56527b92018-03-21 19:31:58 +053082
sunitakx0e0546f2021-06-15 08:57:10 +000083 std::string message = "Invalid login attempted via RCMPP interface ";
Tom Joseph56527b92018-03-21 19:31:58 +053084 // Validate user name length in the message
85 if (request->user_name_len > userNameMaxLen ||
Vernon Mauery9e801a22018-10-12 13:20:49 -070086 inPayload.size() != rakp1Size)
Tom Joseph56527b92018-03-21 19:31:58 +053087 {
88 response->rmcpStatusCode =
89 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH);
sunitakx0e0546f2021-06-15 08:57:10 +000090 logInvalidLoginRedfishEvent(message);
Tom Joseph56527b92018-03-21 19:31:58 +053091 return outPayload;
92 }
93
94 session->userName.assign(request->user_name, request->user_name_len);
95
Tom Joseph8bb10b72016-12-06 17:47:56 +053096 // Update transaction time
97 session->updateLastTransactionTime();
98
99 auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
100 auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
101 auto authAlgo = session->getAuthAlgo();
102
103 /*
104 * Generate Key Authentication Code - RAKP 2
105 *
106 * 1) Remote Console Session ID - 4 bytes
107 * 2) Managed System Session ID - 4 bytes
108 * 3) Remote Console Random Number - 16 bytes
109 * 4) Managed System Random Number - 16 bytes
110 * 5) Managed System GUID - 16 bytes
111 * 6) Requested Privilege Level - 1 byte
112 * 7) User Name Length Byte - 1 byte (0 for 'null' username)
113 * 8) User Name - variable (absent for 'null' username)
114 */
115
116 std::vector<uint8_t> input;
117 input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) +
118 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700119 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN +
120 sizeof(request->req_max_privilege_level) +
121 sizeof(request->user_name_len) + session->userName.size());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530122
123 auto iter = input.begin();
124
125 // Remote Console Session ID
Vernon Mauery9e801a22018-10-12 13:20:49 -0700126 std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
127 iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530128 std::advance(iter, sizeof(rcSessionID));
129
130 // Managed System Session ID
131 std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
132 iter);
133 std::advance(iter, sizeof(bmcSessionID));
134
135 // Copy the Remote Console Random Number from the RAKP1 request to the
136 // Authentication Algorithm
Vernon Mauery9e801a22018-10-12 13:20:49 -0700137 std::copy_n(
138 reinterpret_cast<const uint8_t*>(request->remote_console_random_number),
139 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
140 authAlgo->rcRandomNum.begin());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530141
Vernon Mauery9e801a22018-10-12 13:20:49 -0700142 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530143 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
144
145 // Generate the Managed System Random Number
146 if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700147 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
Tom Joseph8bb10b72016-12-06 17:47:56 +0530148 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN))
149 {
150 response->rmcpStatusCode =
151 static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE);
152 return outPayload;
153 }
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530154 // As stated in Set Session Privilege Level command in IPMI Spec, when
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000155 // creating a session through Activate command / RAKP 1 message, it must
156 // be established with USER privilege as well as all other sessions are
157 // initially set to USER privilege, regardless of the requested maximum
158 // privilege.
159 if (!(static_cast<session::Privilege>(request->req_max_privilege_level &
160 session::reqMaxPrivMask) >
161 session::Privilege::CALLBACK))
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530162 {
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000163 response->rmcpStatusCode =
164 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_ROLE_PRIV);
165 return outPayload;
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530166 }
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000167 session->currentPrivilege(static_cast<uint8_t>(session::Privilege::USER));
168
Tom Joseph4021b1f2019-02-12 10:10:12 +0530169 session->reqMaxPrivLevel =
170 static_cast<session::Privilege>(request->req_max_privilege_level);
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530171 if (request->user_name_len == 0)
Richard Marian Thomaiyar127748a2018-09-06 07:08:51 +0530172 {
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530173 // Bail out, if user name is not specified.
174 // Yes, NULL user name is not supported for security reasons.
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530175 response->rmcpStatusCode =
176 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
sunitakx0e0546f2021-06-15 08:57:10 +0000177 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530178 return outPayload;
Richard Marian Thomaiyard2563c52018-11-29 11:49:10 +0530179 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530180
181 // Perform user name based lookup
182 std::string userName(request->user_name, request->user_name_len);
Jayaprakash Mutyala05c14472020-10-19 10:26:24 +0000183 ipmi::SecureString passwd;
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530184 uint8_t userId = ipmi::ipmiUserGetUserId(userName);
185 if (userId == ipmi::invalidUserId)
186 {
187 response->rmcpStatusCode =
188 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
sunitakx0e0546f2021-06-15 08:57:10 +0000189 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530190 return outPayload;
191 }
192 // check user is enabled before proceeding.
193 bool userEnabled = false;
194 ipmi::ipmiUserCheckEnabled(userId, userEnabled);
195 if (!userEnabled)
196 {
197 response->rmcpStatusCode =
198 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
sunitakx0e0546f2021-06-15 08:57:10 +0000199 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530200 return outPayload;
201 }
Richard Marian Thomaiyar4c4694e2019-06-20 16:36:49 +0530202 // Get the user password for RAKP message authenticate
203 passwd = ipmi::ipmiUserGetPassword(userName);
204 if (passwd.empty())
205 {
206 response->rmcpStatusCode =
207 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
sunitakx0e0546f2021-06-15 08:57:10 +0000208 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar4c4694e2019-06-20 16:36:49 +0530209 return outPayload;
210 }
Ayushi Smritib31e9692019-05-15 12:06:51 +0000211 // Check whether user is already locked for failed attempts
212 if (!ipmi::ipmiUserPamAuthenticate(userName, passwd))
213 {
214 log<level::ERR>("Authentication failed - user already locked out",
215 entry("USER-ID=%d", static_cast<uint8_t>(userId)));
216
217 response->rmcpStatusCode =
218 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
sunitakx0e0546f2021-06-15 08:57:10 +0000219 logInvalidLoginRedfishEvent(message);
Ayushi Smritib31e9692019-05-15 12:06:51 +0000220 return outPayload;
221 }
Saravanan Palanisamyd9c86bb2019-08-07 17:57:37 +0000222
223 uint8_t chNum = static_cast<uint8_t>(getInterfaceIndex());
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530224 // Get channel based access information
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530225 if ((ipmi::ipmiUserGetPrivilegeAccess(
226 userId, chNum, session->sessionUserPrivAccess) != IPMI_CC_OK) ||
227 (ipmi::getChannelAccessData(chNum, session->sessionChannelAccess) !=
228 IPMI_CC_OK))
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530229 {
230 response->rmcpStatusCode =
231 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
sunitakx0e0546f2021-06-15 08:57:10 +0000232 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530233 return outPayload;
234 }
AppaRao Puliecb32fb2020-07-01 22:55:38 +0530235 if (!isChannelAccessModeEnabled(session->sessionChannelAccess.accessMode))
236 {
237 phosphor::logging::log<phosphor::logging::level::ERR>(
238 "Channel access mode disabled.");
239 response->rmcpStatusCode =
240 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
sunitakx0e0546f2021-06-15 08:57:10 +0000241 logInvalidLoginRedfishEvent(message);
AppaRao Puliecb32fb2020-07-01 22:55:38 +0530242 return outPayload;
243 }
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530244 if (session->sessionUserPrivAccess.privilege >
245 static_cast<uint8_t>(session::Privilege::OEM))
Richard Marian Thomaiyar7e5d38d2019-03-02 22:11:41 +0530246 {
247 response->rmcpStatusCode =
248 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
sunitakx0e0546f2021-06-15 08:57:10 +0000249 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyar7e5d38d2019-03-02 22:11:41 +0530250 return outPayload;
251 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530252 session->channelNum(chNum);
253 session->userID(userId);
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000254 // minimum privilege of Channel / User / session::privilege::USER
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530255 // has to be used as session current privilege level
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530256 uint8_t minPriv = 0;
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530257 if (session->sessionChannelAccess.privLimit <
258 session->sessionUserPrivAccess.privilege)
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530259 {
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530260 minPriv = session->sessionChannelAccess.privLimit;
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530261 }
262 else
263 {
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530264 minPriv = session->sessionUserPrivAccess.privilege;
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530265 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530266 if (session->currentPrivilege() > minPriv)
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530267 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530268 session->currentPrivilege(static_cast<uint8_t>(minPriv));
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530269 }
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530270 // For username / privilege lookup, fail with UNAUTH_NAME, if requested
Richard Marian Thomaiyard8e92fe2019-01-16 11:56:23 +0530271 // max privilege does not match user privilege
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530272 if (((request->req_max_privilege_level & userNameOnlyLookupMask) ==
273 userNamePrivLookup) &&
Richard Marian Thomaiyard8e92fe2019-01-16 11:56:23 +0530274 ((request->req_max_privilege_level & session::reqMaxPrivMask) !=
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530275 session->sessionUserPrivAccess.privilege))
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530276 {
Vernon Maueryfc37e592018-12-19 14:55:15 -0800277 log<level::INFO>(
278 "Username/Privilege lookup failed for requested privilege");
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530279 response->rmcpStatusCode =
280 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
sunitakx0e0546f2021-06-15 08:57:10 +0000281
282 logInvalidLoginRedfishEvent(message);
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530283 return outPayload;
284 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530285
286 std::fill(authAlgo->userKey.data(),
287 authAlgo->userKey.data() + authAlgo->userKey.size(), 0);
288 std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data());
289
Tom Joseph8bb10b72016-12-06 17:47:56 +0530290 // Copy the Managed System Random Number to the Authentication Algorithm
291 std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN,
292 authAlgo->bmcRandomNum.begin());
293 std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
294
295 // Managed System GUID
Tom Joseph83029cb2017-09-01 16:37:31 +0530296 std::copy_n(cache::guid.data(), cache::guid.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530297 std::advance(iter, BMC_GUID_LEN);
298
299 // Requested Privilege Level
Tom Joseph8bb10b72016-12-06 17:47:56 +0530300 std::copy_n(&(request->req_max_privilege_level),
301 sizeof(request->req_max_privilege_level), iter);
302 std::advance(iter, sizeof(request->req_max_privilege_level));
303
Tom Joseph8bb10b72016-12-06 17:47:56 +0530304 // User Name Length Byte
305 std::copy_n(&(request->user_name_len), sizeof(request->user_name_len),
306 iter);
Tom Joseph56527b92018-03-21 19:31:58 +0530307 std::advance(iter, sizeof(request->user_name_len));
308
309 std::copy_n(session->userName.data(), session->userName.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530310
311 // Generate Key Exchange Authentication Code - RAKP2
312 auto output = authAlgo->generateHMAC(input);
313
314 response->messageTag = request->messageTag;
315 response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
316 response->reserved = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700317 response->remoteConsoleSessionID = rcSessionID;
Tom Joseph8bb10b72016-12-06 17:47:56 +0530318
319 // Copy Managed System Random Number to the Response
320 std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
321 response->managed_system_random_number);
322
323 // Copy System GUID to the Response
Vernon Mauery9e801a22018-10-12 13:20:49 -0700324 std::copy_n(cache::guid.data(), cache::guid.size(),
Tom Joseph83029cb2017-09-01 16:37:31 +0530325 response->managed_system_guid);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530326
327 // Insert the HMAC output into the payload
328 outPayload.insert(outPayload.end(), output.begin(), output.end());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530329 return outPayload;
330}
331
332} // namespace command