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 <ipmid/types.hpp>
11 #include <phosphor-logging/log.hpp>
12 
13 #include <algorithm>
14 #include <cstring>
15 #include <iomanip>
16 
17 using namespace phosphor::logging;
18 
19 namespace command
20 {
21 
22 bool isChannelAccessModeEnabled(const uint8_t accessMode)
23 {
24     return accessMode !=
25            static_cast<uint8_t>(ipmi::EChannelAccessMode::disabled);
26 }
27 
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 = "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 }
41 std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload,
42                             std::shared_ptr<message::Handler>& handler)
43 {
44     auto request = reinterpret_cast<const RAKP1request*>(inPayload.data());
45     // 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));
53     auto response = reinterpret_cast<RAKP2response*>(outPayload.data());
54 
55     // Session ID zero is reserved for Session Setup
56     if (endian::from_ipmi(request->managedSystemSessionID) ==
57         session::sessionZero)
58     {
59         log<level::INFO>("RAKP12: BMC invalid Session ID");
60         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     {
68         session = session::Manager::get().getSession(
69             endian::from_ipmi(request->managedSystemSessionID));
70     }
71     catch (const std::exception& e)
72     {
73         log<level::ERR>("RAKP12 : session not found",
74                         entry("EXCEPTION=%s", e.what()));
75         response->rmcpStatusCode =
76             static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
77         return outPayload;
78     }
79 
80     auto rakp1Size =
81         sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len);
82 
83     std::string message = "Invalid login attempted via RCMPP interface ";
84     // Validate user name length in the message
85     if (request->user_name_len > userNameMaxLen ||
86         inPayload.size() != rakp1Size)
87     {
88         response->rmcpStatusCode =
89             static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH);
90         logInvalidLoginRedfishEvent(message);
91         return outPayload;
92     }
93 
94     session->userName.assign(request->user_name, request->user_name_len);
95 
96     // 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 +
119                  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());
122 
123     auto iter = input.begin();
124 
125     // Remote Console Session ID
126     std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
127                 iter);
128     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
137     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());
141 
142     std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
143     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) +
147                         cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
148                     cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN))
149     {
150         response->rmcpStatusCode =
151             static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE);
152         return outPayload;
153     }
154     // As stated in Set Session Privilege Level command in IPMI Spec, when
155     // 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))
162     {
163         response->rmcpStatusCode =
164             static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_ROLE_PRIV);
165         return outPayload;
166     }
167     session->currentPrivilege(static_cast<uint8_t>(session::Privilege::USER));
168 
169     session->reqMaxPrivLevel =
170         static_cast<session::Privilege>(request->req_max_privilege_level);
171     if (request->user_name_len == 0)
172     {
173         // Bail out, if user name is not specified.
174         // Yes, NULL user name is not supported for security reasons.
175         response->rmcpStatusCode =
176             static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
177         logInvalidLoginRedfishEvent(message);
178         return outPayload;
179     }
180 
181     // Perform user name based lookup
182     std::string userName(request->user_name, request->user_name_len);
183     ipmi::SecureString passwd;
184     uint8_t userId = ipmi::ipmiUserGetUserId(userName);
185     if (userId == ipmi::invalidUserId)
186     {
187         response->rmcpStatusCode =
188             static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
189         logInvalidLoginRedfishEvent(message);
190         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);
199         logInvalidLoginRedfishEvent(message);
200         return outPayload;
201     }
202     // 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);
208         logInvalidLoginRedfishEvent(message);
209         return outPayload;
210     }
211     // 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);
219         logInvalidLoginRedfishEvent(message);
220         return outPayload;
221     }
222 
223     uint8_t chNum = static_cast<uint8_t>(getInterfaceIndex());
224     // Get channel based access information
225     if ((ipmi::ipmiUserGetPrivilegeAccess(
226              userId, chNum, session->sessionUserPrivAccess) != IPMI_CC_OK) ||
227         (ipmi::getChannelAccessData(chNum, session->sessionChannelAccess) !=
228          IPMI_CC_OK))
229     {
230         response->rmcpStatusCode =
231             static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
232         logInvalidLoginRedfishEvent(message);
233         return outPayload;
234     }
235     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);
241         logInvalidLoginRedfishEvent(message);
242         return outPayload;
243     }
244     if (session->sessionUserPrivAccess.privilege >
245         static_cast<uint8_t>(session::Privilege::OEM))
246     {
247         response->rmcpStatusCode =
248             static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
249         logInvalidLoginRedfishEvent(message);
250         return outPayload;
251     }
252     session->channelNum(chNum);
253     session->userID(userId);
254     // minimum privilege of Channel / User / session::privilege::USER
255     // has to be used as session current privilege level
256     uint8_t minPriv = 0;
257     if (session->sessionChannelAccess.privLimit <
258         session->sessionUserPrivAccess.privilege)
259     {
260         minPriv = session->sessionChannelAccess.privLimit;
261     }
262     else
263     {
264         minPriv = session->sessionUserPrivAccess.privilege;
265     }
266     if (session->currentPrivilege() > minPriv)
267     {
268         session->currentPrivilege(static_cast<uint8_t>(minPriv));
269     }
270     // For username / privilege lookup, fail with UNAUTH_NAME, if requested
271     // max privilege does not match user privilege
272     if (((request->req_max_privilege_level & userNameOnlyLookupMask) ==
273          userNamePrivLookup) &&
274         ((request->req_max_privilege_level & session::reqMaxPrivMask) !=
275          session->sessionUserPrivAccess.privilege))
276     {
277         log<level::INFO>(
278             "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     std::copy_n(cache::guid.data(), cache::guid.size(), iter);
297     std::advance(iter, BMC_GUID_LEN);
298 
299     // Requested Privilege Level
300     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 
304     // User Name Length Byte
305     std::copy_n(&(request->user_name_len), sizeof(request->user_name_len),
306                 iter);
307     std::advance(iter, sizeof(request->user_name_len));
308 
309     std::copy_n(session->userName.data(), session->userName.size(), iter);
310 
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;
317     response->remoteConsoleSessionID = rcSessionID;
318 
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
324     std::copy_n(cache::guid.data(), cache::guid.size(),
325                 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