1 #include "config.h"
2
3 #include "rakp12.hpp"
4
5 #include "comm_module.hpp"
6 #include "endian.hpp"
7 #include "guid.hpp"
8 #include "sessions_manager.hpp"
9
10 #include <openssl/rand.h>
11
12 #include <ipmid/types.hpp>
13 #include <phosphor-logging/lg2.hpp>
14
15 #include <algorithm>
16 #include <cstring>
17 #include <iomanip>
18
19 namespace command
20 {
21
isChannelAccessModeEnabled(const uint8_t accessMode)22 bool isChannelAccessModeEnabled(const uint8_t accessMode)
23 {
24 return accessMode !=
25 static_cast<uint8_t>(ipmi::EChannelAccessMode::disabled);
26 }
27
logInvalidLoginRedfishEvent(const std::string & journalMsg,const std::optional<std::string> & messageArgs)28 void 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 =
33 "OpenBMC." + std::string(openBMCMessageRegistryVersion) +
34 "InvalidLoginAttempted";
35 lg2::error(
36 "message: {MSG}, id: {REDFISH_MESSAGE_ID}, args: {REDFISH_MESSAGE_ARGS}",
37 "MSG", journalMsg, "REDFISH_MESSAGE_ID", messageID,
38 "REDFISH_MESSAGE_ARGS", messageArgs.value_or(std::string{}));
39 }
RAKP12(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> &)40 std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload,
41 std::shared_ptr<message::Handler>& /* handler */)
42 {
43 auto request = reinterpret_cast<const RAKP1request*>(inPayload.data());
44 // verify inPayload minimum size
45 if (inPayload.size() < (sizeof(*request) - userNameMaxLen))
46 {
47 std::vector<uint8_t> errorPayload{IPMI_CC_REQ_DATA_LEN_INVALID};
48 return errorPayload;
49 }
50
51 std::vector<uint8_t> outPayload(sizeof(RAKP2response));
52 auto response = reinterpret_cast<RAKP2response*>(outPayload.data());
53
54 // Session ID zero is reserved for Session Setup
55 if (endian::from_ipmi(request->managedSystemSessionID) ==
56 session::sessionZero)
57 {
58 lg2::info("RAKP12: BMC invalid Session ID");
59 response->rmcpStatusCode =
60 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
61 return outPayload;
62 }
63
64 std::shared_ptr<session::Session> session;
65 try
66 {
67 session = session::Manager::get().getSession(
68 endian::from_ipmi(request->managedSystemSessionID));
69 }
70 catch (const std::exception& e)
71 {
72 lg2::error("RAKP12 : session not found: {ERROR}", "ERROR", e);
73 response->rmcpStatusCode =
74 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
75 return outPayload;
76 }
77
78 auto rakp1Size =
79 sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len);
80
81 std::string message = "Invalid login attempted via RCMPP interface ";
82 // Validate user name length in the message
83 if (request->user_name_len > userNameMaxLen ||
84 inPayload.size() != rakp1Size)
85 {
86 response->rmcpStatusCode =
87 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH);
88 logInvalidLoginRedfishEvent(message);
89 return outPayload;
90 }
91
92 session->userName.assign(request->user_name, request->user_name_len);
93
94 // Update transaction time
95 session->updateLastTransactionTime();
96
97 auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
98 auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
99 auto authAlgo = session->getAuthAlgo();
100
101 /*
102 * Generate Key Authentication Code - RAKP 2
103 *
104 * 1) Remote Console Session ID - 4 bytes
105 * 2) Managed System Session ID - 4 bytes
106 * 3) Remote Console Random Number - 16 bytes
107 * 4) Managed System Random Number - 16 bytes
108 * 5) Managed System GUID - 16 bytes
109 * 6) Requested Privilege Level - 1 byte
110 * 7) User Name Length Byte - 1 byte (0 for 'null' username)
111 * 8) User Name - variable (absent for 'null' username)
112 */
113
114 std::vector<uint8_t> input;
115 input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) +
116 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
117 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN +
118 sizeof(request->req_max_privilege_level) +
119 sizeof(request->user_name_len) + session->userName.size());
120
121 auto iter = input.begin();
122
123 // Remote Console Session ID
124 std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
125 iter);
126 std::advance(iter, sizeof(rcSessionID));
127
128 // Managed System Session ID
129 std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
130 iter);
131 std::advance(iter, sizeof(bmcSessionID));
132
133 // Copy the Remote Console Random Number from the RAKP1 request to the
134 // Authentication Algorithm
135 std::copy_n(
136 reinterpret_cast<const uint8_t*>(request->remote_console_random_number),
137 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
138 authAlgo->rcRandomNum.begin());
139
140 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
141 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
142
143 // Generate the Managed System Random Number
144 if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) +
145 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
146 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN))
147 {
148 response->rmcpStatusCode =
149 static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE);
150 return outPayload;
151 }
152 // As stated in Set Session Privilege Level command in IPMI Spec, when
153 // creating a session through Activate command / RAKP 1 message, it must
154 // be established with USER privilege as well as all other sessions are
155 // initially set to USER privilege, regardless of the requested maximum
156 // privilege.
157 if (!(static_cast<session::Privilege>(
158 request->req_max_privilege_level & session::reqMaxPrivMask) >
159 session::Privilege::CALLBACK))
160 {
161 response->rmcpStatusCode =
162 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_ROLE_PRIV);
163 return outPayload;
164 }
165 session->currentPrivilege(static_cast<uint8_t>(session::Privilege::USER));
166
167 session->reqMaxPrivLevel =
168 static_cast<session::Privilege>(request->req_max_privilege_level);
169 if (request->user_name_len == 0)
170 {
171 // Bail out, if user name is not specified.
172 // Yes, NULL user name is not supported for security reasons.
173 response->rmcpStatusCode =
174 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
175 logInvalidLoginRedfishEvent(message);
176 return outPayload;
177 }
178
179 // Perform user name based lookup
180 std::string userName(request->user_name, request->user_name_len);
181 ipmi::SecureString passwd;
182 uint8_t userId = ipmi::ipmiUserGetUserId(userName);
183 if (userId == ipmi::invalidUserId)
184 {
185 response->rmcpStatusCode =
186 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
187 logInvalidLoginRedfishEvent(message);
188 return outPayload;
189 }
190 // check user is enabled before proceeding.
191 bool userEnabled = false;
192 ipmi::ipmiUserCheckEnabled(userId, userEnabled);
193 if (!userEnabled)
194 {
195 response->rmcpStatusCode =
196 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
197 logInvalidLoginRedfishEvent(message);
198 return outPayload;
199 }
200 // Get the user password for RAKP message authenticate
201 passwd = ipmi::ipmiUserGetPassword(userName);
202 if (passwd.empty())
203 {
204 response->rmcpStatusCode =
205 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
206 logInvalidLoginRedfishEvent(message);
207 return outPayload;
208 }
209 #ifdef PAM_AUTHENTICATE
210 // Check whether user is already locked for failed attempts
211 if (!ipmi::ipmiUserPamAuthenticate(userName, passwd))
212 {
213 lg2::error(
214 "Authentication failed - user already locked out, user id: {ID}",
215 "ID", userId);
216
217 response->rmcpStatusCode =
218 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
219 logInvalidLoginRedfishEvent(message);
220 return outPayload;
221 }
222 #endif
223
224 uint8_t chNum = static_cast<uint8_t>(getInterfaceIndex());
225 // Get channel based access information
226 if ((ipmi::ipmiUserGetPrivilegeAccess(userId, chNum,
227 session->sessionUserPrivAccess) !=
228 ipmi::ccSuccess) ||
229 (ipmi::getChannelAccessData(chNum, session->sessionChannelAccess) !=
230 ipmi::ccSuccess))
231 {
232 response->rmcpStatusCode =
233 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
234 logInvalidLoginRedfishEvent(message);
235 return outPayload;
236 }
237 if (!isChannelAccessModeEnabled(session->sessionChannelAccess.accessMode))
238 {
239 lg2::error("Channel access mode disabled.");
240 response->rmcpStatusCode =
241 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
242 logInvalidLoginRedfishEvent(message);
243 return outPayload;
244 }
245 if (session->sessionUserPrivAccess.privilege >
246 static_cast<uint8_t>(session::Privilege::OEM))
247 {
248 response->rmcpStatusCode =
249 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
250 logInvalidLoginRedfishEvent(message);
251 return outPayload;
252 }
253 session->channelNum(chNum);
254 session->userID(userId);
255 // minimum privilege of Channel / User / session::privilege::USER
256 // has to be used as session current privilege level
257 uint8_t minPriv = 0;
258 if (session->sessionChannelAccess.privLimit <
259 session->sessionUserPrivAccess.privilege)
260 {
261 minPriv = session->sessionChannelAccess.privLimit;
262 }
263 else
264 {
265 minPriv = session->sessionUserPrivAccess.privilege;
266 }
267 if (session->currentPrivilege() > minPriv)
268 {
269 session->currentPrivilege(static_cast<uint8_t>(minPriv));
270 }
271 // For username / privilege lookup, fail with UNAUTH_NAME, if requested
272 // max privilege does not match user privilege
273 if (((request->req_max_privilege_level & userNameOnlyLookupMask) ==
274 userNamePrivLookup) &&
275 ((request->req_max_privilege_level & session::reqMaxPrivMask) !=
276 session->sessionUserPrivAccess.privilege))
277 {
278 lg2::info("Username/Privilege lookup failed for requested privilege");
279 response->rmcpStatusCode =
280 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
281
282 logInvalidLoginRedfishEvent(message);
283 return outPayload;
284 }
285
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
290 // 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
296 const Guid& guid = command::getSystemGUID();
297 std::copy_n(guid.data(), guid.size(), iter);
298 std::advance(iter, BMC_GUID_LEN);
299
300 // Requested Privilege Level
301 std::copy_n(&(request->req_max_privilege_level),
302 sizeof(request->req_max_privilege_level), iter);
303 std::advance(iter, sizeof(request->req_max_privilege_level));
304
305 // User Name Length Byte
306 std::copy_n(&(request->user_name_len), sizeof(request->user_name_len),
307 iter);
308 std::advance(iter, sizeof(request->user_name_len));
309
310 std::copy_n(session->userName.data(), session->userName.size(), iter);
311
312 // Generate Key Exchange Authentication Code - RAKP2
313 auto output = authAlgo->generateHMAC(input);
314
315 response->messageTag = request->messageTag;
316 response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
317 response->reserved = 0;
318 response->remoteConsoleSessionID = rcSessionID;
319
320 // Copy Managed System Random Number to the Response
321 std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
322 response->managed_system_random_number);
323
324 // Copy System GUID to the Response
325 std::copy_n(guid.data(), guid.size(), response->managed_system_guid);
326
327 // Insert the HMAC output into the payload
328 outPayload.insert(outPayload.end(), output.begin(), output.end());
329 return outPayload;
330 }
331
332 } // namespace command
333