xref: /openbmc/phosphor-net-ipmid/command/rakp12.cpp (revision 067d178bcc0c572afad75ceadf3c55f293639e8a)
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