blob: 172c83e9ff14ec5587092e164962e1341cd09c6f [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
Tom Joseph18a45e92017-04-11 11:30:44 +053020std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload,
Tom Joseph8bb10b72016-12-06 17:47:56 +053021 const message::Handler& handler)
22{
Tom Joseph8bb10b72016-12-06 17:47:56 +053023 std::vector<uint8_t> outPayload(sizeof(RAKP2response));
Tom Joseph18a45e92017-04-11 11:30:44 +053024 auto request = reinterpret_cast<const RAKP1request*>(inPayload.data());
Tom Joseph8bb10b72016-12-06 17:47:56 +053025 auto response = reinterpret_cast<RAKP2response*>(outPayload.data());
26
27 // Session ID zero is reserved for Session Setup
Vernon Mauery9e801a22018-10-12 13:20:49 -070028 if (endian::from_ipmi(request->managedSystemSessionID) ==
29 session::SESSION_ZERO)
Tom Joseph8bb10b72016-12-06 17:47:56 +053030 {
Vernon Maueryfc37e592018-12-19 14:55:15 -080031 log<level::INFO>("RAKP12: BMC invalid Session ID");
Tom Joseph8bb10b72016-12-06 17:47:56 +053032 response->rmcpStatusCode =
33 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
34 return outPayload;
35 }
36
37 std::shared_ptr<session::Session> session;
38 try
39 {
Vernon Maueryae1fda42018-10-15 12:55:34 -070040 session =
41 std::get<session::Manager&>(singletonPool)
42 .getSession(endian::from_ipmi(request->managedSystemSessionID));
Tom Joseph8bb10b72016-12-06 17:47:56 +053043 }
44 catch (std::exception& e)
45 {
Vernon Maueryfc37e592018-12-19 14:55:15 -080046 log<level::ERR>("RAKP12 : session not found",
47 entry("EXCEPTION=%s", e.what()));
Tom Joseph8bb10b72016-12-06 17:47:56 +053048 response->rmcpStatusCode =
49 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
50 return outPayload;
51 }
52
Vernon Mauery9e801a22018-10-12 13:20:49 -070053 auto rakp1Size =
54 sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len);
Tom Joseph56527b92018-03-21 19:31:58 +053055
56 // Validate user name length in the message
57 if (request->user_name_len > userNameMaxLen ||
Vernon Mauery9e801a22018-10-12 13:20:49 -070058 inPayload.size() != rakp1Size)
Tom Joseph56527b92018-03-21 19:31:58 +053059 {
60 response->rmcpStatusCode =
61 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH);
62 return outPayload;
63 }
64
65 session->userName.assign(request->user_name, request->user_name_len);
66
Tom Joseph8bb10b72016-12-06 17:47:56 +053067 // Update transaction time
68 session->updateLastTransactionTime();
69
70 auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
71 auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
72 auto authAlgo = session->getAuthAlgo();
73
74 /*
75 * Generate Key Authentication Code - RAKP 2
76 *
77 * 1) Remote Console Session ID - 4 bytes
78 * 2) Managed System Session ID - 4 bytes
79 * 3) Remote Console Random Number - 16 bytes
80 * 4) Managed System Random Number - 16 bytes
81 * 5) Managed System GUID - 16 bytes
82 * 6) Requested Privilege Level - 1 byte
83 * 7) User Name Length Byte - 1 byte (0 for 'null' username)
84 * 8) User Name - variable (absent for 'null' username)
85 */
86
87 std::vector<uint8_t> input;
88 input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) +
89 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
Vernon Mauery9e801a22018-10-12 13:20:49 -070090 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN +
91 sizeof(request->req_max_privilege_level) +
92 sizeof(request->user_name_len) + session->userName.size());
Tom Joseph8bb10b72016-12-06 17:47:56 +053093
94 auto iter = input.begin();
95
96 // Remote Console Session ID
Vernon Mauery9e801a22018-10-12 13:20:49 -070097 std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
98 iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +053099 std::advance(iter, sizeof(rcSessionID));
100
101 // Managed System Session ID
102 std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
103 iter);
104 std::advance(iter, sizeof(bmcSessionID));
105
106 // Copy the Remote Console Random Number from the RAKP1 request to the
107 // Authentication Algorithm
Vernon Mauery9e801a22018-10-12 13:20:49 -0700108 std::copy_n(
109 reinterpret_cast<const uint8_t*>(request->remote_console_random_number),
110 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
111 authAlgo->rcRandomNum.begin());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530112
Vernon Mauery9e801a22018-10-12 13:20:49 -0700113 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530114 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
115
116 // Generate the Managed System Random Number
117 if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700118 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
Tom Joseph8bb10b72016-12-06 17:47:56 +0530119 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN))
120 {
121 response->rmcpStatusCode =
122 static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE);
123 return outPayload;
124 }
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530125 // As stated in Set Session Privilege Level command in IPMI Spec, when
126 // creating a session through Activate command / RAKP 1 message, it must be
127 // established with CALLBACK privilege if requested for callback. All other
128 // sessions are initialy set to USER privilege, regardless of the requested
129 // maximum privilege.
130 session->curPrivLevel = session::Privilege::CALLBACK;
131 if (static_cast<session::Privilege>(request->req_max_privilege_level &
132 session::reqMaxPrivMask) >
133 session::Privilege::CALLBACK)
134 {
135 session->curPrivLevel = session::Privilege::USER;
136 }
Tom Joseph4021b1f2019-02-12 10:10:12 +0530137 session->reqMaxPrivLevel =
138 static_cast<session::Privilege>(request->req_max_privilege_level);
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530139 if (request->user_name_len == 0)
Richard Marian Thomaiyar127748a2018-09-06 07:08:51 +0530140 {
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530141 // Bail out, if user name is not specified.
142 // Yes, NULL user name is not supported for security reasons.
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530143 response->rmcpStatusCode =
144 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
145 return outPayload;
Richard Marian Thomaiyard2563c52018-11-29 11:49:10 +0530146 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530147
148 // Perform user name based lookup
149 std::string userName(request->user_name, request->user_name_len);
150 std::string passwd;
151 uint8_t userId = ipmi::ipmiUserGetUserId(userName);
152 if (userId == ipmi::invalidUserId)
153 {
154 response->rmcpStatusCode =
155 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
156 return outPayload;
157 }
158 // check user is enabled before proceeding.
159 bool userEnabled = false;
160 ipmi::ipmiUserCheckEnabled(userId, userEnabled);
161 if (!userEnabled)
162 {
163 response->rmcpStatusCode =
164 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
165 return outPayload;
166 }
Richard Marian Thomaiyar4c4694e2019-06-20 16:36:49 +0530167 // Get the user password for RAKP message authenticate
168 passwd = ipmi::ipmiUserGetPassword(userName);
169 if (passwd.empty())
170 {
171 response->rmcpStatusCode =
172 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
173 return outPayload;
174 }
Ayushi Smritib31e9692019-05-15 12:06:51 +0000175 // Check whether user is already locked for failed attempts
176 if (!ipmi::ipmiUserPamAuthenticate(userName, passwd))
177 {
178 log<level::ERR>("Authentication failed - user already locked out",
179 entry("USER-ID=%d", static_cast<uint8_t>(userId)));
180
181 response->rmcpStatusCode =
182 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
183 return outPayload;
184 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530185 // TODO Replace with proper calls.
186 uint8_t chNum = static_cast<uint8_t>(ipmi::EChannelID::chanLan1);
187 // Get channel based access information
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530188 if ((ipmi::ipmiUserGetPrivilegeAccess(
189 userId, chNum, session->sessionUserPrivAccess) != IPMI_CC_OK) ||
190 (ipmi::getChannelAccessData(chNum, session->sessionChannelAccess) !=
191 IPMI_CC_OK))
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530192 {
193 response->rmcpStatusCode =
194 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
195 return outPayload;
196 }
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530197 if (session->sessionUserPrivAccess.privilege >
198 static_cast<uint8_t>(session::Privilege::OEM))
Richard Marian Thomaiyar7e5d38d2019-03-02 22:11:41 +0530199 {
200 response->rmcpStatusCode =
201 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
202 return outPayload;
203 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530204 session->chNum = chNum;
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530205 // minimum privilege of Channel / User / session::privilege::USER/CALLBACK /
206 // has to be used as session current privilege level
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530207 uint8_t minPriv = 0;
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530208 if (session->sessionChannelAccess.privLimit <
209 session->sessionUserPrivAccess.privilege)
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530210 {
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530211 minPriv = session->sessionChannelAccess.privLimit;
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530212 }
213 else
214 {
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530215 minPriv = session->sessionUserPrivAccess.privilege;
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530216 }
217 if (session->curPrivLevel > static_cast<session::Privilege>(minPriv))
218 {
219 session->curPrivLevel = static_cast<session::Privilege>(minPriv);
220 }
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530221 // For username / privilege lookup, fail with UNAUTH_NAME, if requested
Richard Marian Thomaiyard8e92fe2019-01-16 11:56:23 +0530222 // max privilege does not match user privilege
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530223 if (((request->req_max_privilege_level & userNameOnlyLookupMask) ==
224 userNamePrivLookup) &&
Richard Marian Thomaiyard8e92fe2019-01-16 11:56:23 +0530225 ((request->req_max_privilege_level & session::reqMaxPrivMask) !=
Richard Marian Thomaiyar992e53c2019-03-03 13:30:46 +0530226 session->sessionUserPrivAccess.privilege))
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530227 {
Vernon Maueryfc37e592018-12-19 14:55:15 -0800228 log<level::INFO>(
229 "Username/Privilege lookup failed for requested privilege");
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530230 response->rmcpStatusCode =
231 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
232 return outPayload;
233 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530234
235 std::fill(authAlgo->userKey.data(),
236 authAlgo->userKey.data() + authAlgo->userKey.size(), 0);
237 std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data());
238
Tom Joseph8bb10b72016-12-06 17:47:56 +0530239 // Copy the Managed System Random Number to the Authentication Algorithm
240 std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN,
241 authAlgo->bmcRandomNum.begin());
242 std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
243
244 // Managed System GUID
Tom Joseph83029cb2017-09-01 16:37:31 +0530245 std::copy_n(cache::guid.data(), cache::guid.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530246 std::advance(iter, BMC_GUID_LEN);
247
248 // Requested Privilege Level
Tom Joseph8bb10b72016-12-06 17:47:56 +0530249 std::copy_n(&(request->req_max_privilege_level),
250 sizeof(request->req_max_privilege_level), iter);
251 std::advance(iter, sizeof(request->req_max_privilege_level));
252
Tom Joseph8bb10b72016-12-06 17:47:56 +0530253 // User Name Length Byte
254 std::copy_n(&(request->user_name_len), sizeof(request->user_name_len),
255 iter);
Tom Joseph56527b92018-03-21 19:31:58 +0530256 std::advance(iter, sizeof(request->user_name_len));
257
258 std::copy_n(session->userName.data(), session->userName.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530259
260 // Generate Key Exchange Authentication Code - RAKP2
261 auto output = authAlgo->generateHMAC(input);
262
263 response->messageTag = request->messageTag;
264 response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
265 response->reserved = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700266 response->remoteConsoleSessionID = rcSessionID;
Tom Joseph8bb10b72016-12-06 17:47:56 +0530267
268 // Copy Managed System Random Number to the Response
269 std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
270 response->managed_system_random_number);
271
272 // Copy System GUID to the Response
Vernon Mauery9e801a22018-10-12 13:20:49 -0700273 std::copy_n(cache::guid.data(), cache::guid.size(),
Tom Joseph83029cb2017-09-01 16:37:31 +0530274 response->managed_system_guid);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530275
276 // Insert the HMAC output into the payload
277 outPayload.insert(outPayload.end(), output.begin(), output.end());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530278 return outPayload;
279}
280
281} // namespace command