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