blob: 9a31dfe0ce035a4563a50d47777d79cc210e9032 [file] [log] [blame]
Tom Joseph8bb10b72016-12-06 17:47:56 +05301#include "rakp12.hpp"
2
Vernon Mauery9e801a22018-10-12 13:20:49 -07003#include "comm_module.hpp"
4#include "endian.hpp"
5#include "guid.hpp"
6#include "main.hpp"
7
Tom Joseph8bb10b72016-12-06 17:47:56 +05308#include <openssl/rand.h>
9
10#include <algorithm>
Tom Joseph56527b92018-03-21 19:31:58 +053011#include <cstring>
Tom Joseph8bb10b72016-12-06 17:47:56 +053012#include <iomanip>
Vernon Maueryfc37e592018-12-19 14:55:15 -080013#include <phosphor-logging/log.hpp>
Tom Joseph8bb10b72016-12-06 17:47:56 +053014
Vernon Maueryfc37e592018-12-19 14:55:15 -080015using namespace phosphor::logging;
16
Tom Joseph8bb10b72016-12-06 17:47:56 +053017namespace command
18{
19
AppaRao Puliecb32fb2020-07-01 22:55:38 +053020bool isChannelAccessModeEnabled(const uint8_t accessMode)
21{
22 return accessMode !=
23 static_cast<uint8_t>(ipmi::EChannelAccessMode::disabled);
24}
25
Tom Joseph18a45e92017-04-11 11:30:44 +053026std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload,
Tom Joseph8bb10b72016-12-06 17:47:56 +053027 const message::Handler& handler)
28{
Tom Joseph8bb10b72016-12-06 17:47:56 +053029 std::vector<uint8_t> outPayload(sizeof(RAKP2response));
Tom Joseph18a45e92017-04-11 11:30:44 +053030 auto request = reinterpret_cast<const RAKP1request*>(inPayload.data());
Tom Joseph8bb10b72016-12-06 17:47:56 +053031 auto response = reinterpret_cast<RAKP2response*>(outPayload.data());
32
33 // Session ID zero is reserved for Session Setup
Vernon Mauery9e801a22018-10-12 13:20:49 -070034 if (endian::from_ipmi(request->managedSystemSessionID) ==
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053035 session::sessionZero)
Tom Joseph8bb10b72016-12-06 17:47:56 +053036 {
Vernon Maueryfc37e592018-12-19 14:55:15 -080037 log<level::INFO>("RAKP12: BMC invalid Session ID");
Tom Joseph8bb10b72016-12-06 17:47:56 +053038 response->rmcpStatusCode =
39 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
40 return outPayload;
41 }
42
43 std::shared_ptr<session::Session> session;
44 try
45 {
Vernon Maueryae1fda42018-10-15 12:55:34 -070046 session =
47 std::get<session::Manager&>(singletonPool)
48 .getSession(endian::from_ipmi(request->managedSystemSessionID));
Tom Joseph8bb10b72016-12-06 17:47:56 +053049 }
50 catch (std::exception& e)
51 {
Vernon Maueryfc37e592018-12-19 14:55:15 -080052 log<level::ERR>("RAKP12 : session not found",
53 entry("EXCEPTION=%s", e.what()));
Tom Joseph8bb10b72016-12-06 17:47:56 +053054 response->rmcpStatusCode =
55 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
56 return outPayload;
57 }
58
Vernon Mauery9e801a22018-10-12 13:20:49 -070059 auto rakp1Size =
60 sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len);
Tom Joseph56527b92018-03-21 19:31:58 +053061
62 // Validate user name length in the message
63 if (request->user_name_len > userNameMaxLen ||
Vernon Mauery9e801a22018-10-12 13:20:49 -070064 inPayload.size() != rakp1Size)
Tom Joseph56527b92018-03-21 19:31:58 +053065 {
66 response->rmcpStatusCode =
67 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH);
68 return outPayload;
69 }
70
71 session->userName.assign(request->user_name, request->user_name_len);
72
Tom Joseph8bb10b72016-12-06 17:47:56 +053073 // Update transaction time
74 session->updateLastTransactionTime();
75
76 auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
77 auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
78 auto authAlgo = session->getAuthAlgo();
79
80 /*
81 * Generate Key Authentication Code - RAKP 2
82 *
83 * 1) Remote Console Session ID - 4 bytes
84 * 2) Managed System Session ID - 4 bytes
85 * 3) Remote Console Random Number - 16 bytes
86 * 4) Managed System Random Number - 16 bytes
87 * 5) Managed System GUID - 16 bytes
88 * 6) Requested Privilege Level - 1 byte
89 * 7) User Name Length Byte - 1 byte (0 for 'null' username)
90 * 8) User Name - variable (absent for 'null' username)
91 */
92
93 std::vector<uint8_t> input;
94 input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) +
95 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
Vernon Mauery9e801a22018-10-12 13:20:49 -070096 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN +
97 sizeof(request->req_max_privilege_level) +
98 sizeof(request->user_name_len) + session->userName.size());
Tom Joseph8bb10b72016-12-06 17:47:56 +053099
100 auto iter = input.begin();
101
102 // Remote Console Session ID
Vernon Mauery9e801a22018-10-12 13:20:49 -0700103 std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
104 iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530105 std::advance(iter, sizeof(rcSessionID));
106
107 // Managed System Session ID
108 std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
109 iter);
110 std::advance(iter, sizeof(bmcSessionID));
111
112 // Copy the Remote Console Random Number from the RAKP1 request to the
113 // Authentication Algorithm
Vernon Mauery9e801a22018-10-12 13:20:49 -0700114 std::copy_n(
115 reinterpret_cast<const uint8_t*>(request->remote_console_random_number),
116 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
117 authAlgo->rcRandomNum.begin());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530118
Vernon Mauery9e801a22018-10-12 13:20:49 -0700119 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530120 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
121
122 // Generate the Managed System Random Number
123 if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700124 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
Tom Joseph8bb10b72016-12-06 17:47:56 +0530125 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN))
126 {
127 response->rmcpStatusCode =
128 static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE);
129 return outPayload;
130 }
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530131 // As stated in Set Session Privilege Level command in IPMI Spec, when
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000132 // creating a session through Activate command / RAKP 1 message, it must
133 // be established with USER privilege as well as all other sessions are
134 // initially set to USER privilege, regardless of the requested maximum
135 // privilege.
136 if (!(static_cast<session::Privilege>(request->req_max_privilege_level &
137 session::reqMaxPrivMask) >
138 session::Privilege::CALLBACK))
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530139 {
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000140 response->rmcpStatusCode =
141 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_ROLE_PRIV);
142 return outPayload;
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530143 }
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000144 session->currentPrivilege(static_cast<uint8_t>(session::Privilege::USER));
145
Tom Joseph4021b1f2019-02-12 10:10:12 +0530146 session->reqMaxPrivLevel =
147 static_cast<session::Privilege>(request->req_max_privilege_level);
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530148 if (request->user_name_len == 0)
Richard Marian Thomaiyar127748a2018-09-06 07:08:51 +0530149 {
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530150 // Bail out, if user name is not specified.
151 // Yes, NULL user name is not supported for security reasons.
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530152 response->rmcpStatusCode =
153 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
154 return outPayload;
Richard Marian Thomaiyard2563c52018-11-29 11:49:10 +0530155 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530156
157 // Perform user name based lookup
158 std::string userName(request->user_name, request->user_name_len);
159 std::string passwd;
160 uint8_t userId = ipmi::ipmiUserGetUserId(userName);
161 if (userId == ipmi::invalidUserId)
162 {
163 response->rmcpStatusCode =
164 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
165 return outPayload;
166 }
167 // check user is enabled before proceeding.
168 bool userEnabled = false;
169 ipmi::ipmiUserCheckEnabled(userId, userEnabled);
170 if (!userEnabled)
171 {
172 response->rmcpStatusCode =
173 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
174 return outPayload;
175 }
Richard Marian Thomaiyar4c4694e2019-06-20 16:36:49 +0530176 // Get the user password for RAKP message authenticate
177 passwd = ipmi::ipmiUserGetPassword(userName);
178 if (passwd.empty())
179 {
180 response->rmcpStatusCode =
181 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
182 return outPayload;
183 }
Ayushi Smritib31e9692019-05-15 12:06:51 +0000184 // Check whether user is already locked for failed attempts
185 if (!ipmi::ipmiUserPamAuthenticate(userName, passwd))
186 {
187 log<level::ERR>("Authentication failed - user already locked out",
188 entry("USER-ID=%d", static_cast<uint8_t>(userId)));
189
190 response->rmcpStatusCode =
191 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
192 return outPayload;
193 }
Saravanan Palanisamyd9c86bb2019-08-07 17:57:37 +0000194
195 uint8_t chNum = static_cast<uint8_t>(getInterfaceIndex());
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530196 // Get channel based access information
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530197 if ((ipmi::ipmiUserGetPrivilegeAccess(
198 userId, chNum, session->sessionUserPrivAccess) != IPMI_CC_OK) ||
199 (ipmi::getChannelAccessData(chNum, session->sessionChannelAccess) !=
200 IPMI_CC_OK))
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530201 {
202 response->rmcpStatusCode =
203 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
204 return outPayload;
205 }
AppaRao Puliecb32fb2020-07-01 22:55:38 +0530206 if (!isChannelAccessModeEnabled(session->sessionChannelAccess.accessMode))
207 {
208 phosphor::logging::log<phosphor::logging::level::ERR>(
209 "Channel access mode disabled.");
210 response->rmcpStatusCode =
211 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
212 return outPayload;
213 }
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530214 if (session->sessionUserPrivAccess.privilege >
215 static_cast<uint8_t>(session::Privilege::OEM))
Richard Marian Thomaiyar7e5d38d2019-03-02 22:11:41 +0530216 {
217 response->rmcpStatusCode =
218 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
219 return outPayload;
220 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530221 session->channelNum(chNum);
222 session->userID(userId);
jayaprakash Mutyala2555e2e2019-12-24 22:51:46 +0000223 // minimum privilege of Channel / User / session::privilege::USER
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530224 // has to be used as session current privilege level
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530225 uint8_t minPriv = 0;
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530226 if (session->sessionChannelAccess.privLimit <
227 session->sessionUserPrivAccess.privilege)
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530228 {
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530229 minPriv = session->sessionChannelAccess.privLimit;
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530230 }
231 else
232 {
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530233 minPriv = session->sessionUserPrivAccess.privilege;
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530234 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530235 if (session->currentPrivilege() > minPriv)
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530236 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530237 session->currentPrivilege(static_cast<uint8_t>(minPriv));
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530238 }
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530239 // For username / privilege lookup, fail with UNAUTH_NAME, if requested
Richard Marian Thomaiyard8e92fe2019-01-16 11:56:23 +0530240 // max privilege does not match user privilege
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530241 if (((request->req_max_privilege_level & userNameOnlyLookupMask) ==
242 userNamePrivLookup) &&
Richard Marian Thomaiyard8e92fe2019-01-16 11:56:23 +0530243 ((request->req_max_privilege_level & session::reqMaxPrivMask) !=
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530244 session->sessionUserPrivAccess.privilege))
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530245 {
Vernon Maueryfc37e592018-12-19 14:55:15 -0800246 log<level::INFO>(
247 "Username/Privilege lookup failed for requested privilege");
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530248 response->rmcpStatusCode =
249 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
250 return outPayload;
251 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530252
253 std::fill(authAlgo->userKey.data(),
254 authAlgo->userKey.data() + authAlgo->userKey.size(), 0);
255 std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data());
256
Tom Joseph8bb10b72016-12-06 17:47:56 +0530257 // Copy the Managed System Random Number to the Authentication Algorithm
258 std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN,
259 authAlgo->bmcRandomNum.begin());
260 std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
261
262 // Managed System GUID
Tom Joseph83029cb2017-09-01 16:37:31 +0530263 std::copy_n(cache::guid.data(), cache::guid.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530264 std::advance(iter, BMC_GUID_LEN);
265
266 // Requested Privilege Level
Tom Joseph8bb10b72016-12-06 17:47:56 +0530267 std::copy_n(&(request->req_max_privilege_level),
268 sizeof(request->req_max_privilege_level), iter);
269 std::advance(iter, sizeof(request->req_max_privilege_level));
270
Tom Joseph8bb10b72016-12-06 17:47:56 +0530271 // User Name Length Byte
272 std::copy_n(&(request->user_name_len), sizeof(request->user_name_len),
273 iter);
Tom Joseph56527b92018-03-21 19:31:58 +0530274 std::advance(iter, sizeof(request->user_name_len));
275
276 std::copy_n(session->userName.data(), session->userName.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530277
278 // Generate Key Exchange Authentication Code - RAKP2
279 auto output = authAlgo->generateHMAC(input);
280
281 response->messageTag = request->messageTag;
282 response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
283 response->reserved = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700284 response->remoteConsoleSessionID = rcSessionID;
Tom Joseph8bb10b72016-12-06 17:47:56 +0530285
286 // Copy Managed System Random Number to the Response
287 std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
288 response->managed_system_random_number);
289
290 // Copy System GUID to the Response
Vernon Mauery9e801a22018-10-12 13:20:49 -0700291 std::copy_n(cache::guid.data(), cache::guid.size(),
Tom Joseph83029cb2017-09-01 16:37:31 +0530292 response->managed_system_guid);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530293
294 // Insert the HMAC output into the payload
295 outPayload.insert(outPayload.end(), output.begin(), output.end());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530296 return outPayload;
297}
298
299} // namespace command