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