blob: 17a748350b15f05fe305166aa6edfa611ee3a103 [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>
Richard Marian Thomaiyar127748a2018-09-06 07:08:51 +053014#include <user_channel/channel_layer.hpp>
15#include <user_channel/user_layer.hpp>
Tom Joseph8bb10b72016-12-06 17:47:56 +053016
Vernon Maueryfc37e592018-12-19 14:55:15 -080017using namespace phosphor::logging;
18
Tom Joseph8bb10b72016-12-06 17:47:56 +053019namespace command
20{
21
Tom Joseph18a45e92017-04-11 11:30:44 +053022std::vector<uint8_t> RAKP12(const std::vector<uint8_t>& inPayload,
Tom Joseph8bb10b72016-12-06 17:47:56 +053023 const message::Handler& handler)
24{
Tom Joseph8bb10b72016-12-06 17:47:56 +053025 std::vector<uint8_t> outPayload(sizeof(RAKP2response));
Tom Joseph18a45e92017-04-11 11:30:44 +053026 auto request = reinterpret_cast<const RAKP1request*>(inPayload.data());
Tom Joseph8bb10b72016-12-06 17:47:56 +053027 auto response = reinterpret_cast<RAKP2response*>(outPayload.data());
28
29 // Session ID zero is reserved for Session Setup
Vernon Mauery9e801a22018-10-12 13:20:49 -070030 if (endian::from_ipmi(request->managedSystemSessionID) ==
31 session::SESSION_ZERO)
Tom Joseph8bb10b72016-12-06 17:47:56 +053032 {
Vernon Maueryfc37e592018-12-19 14:55:15 -080033 log<level::INFO>("RAKP12: BMC invalid Session ID");
Tom Joseph8bb10b72016-12-06 17:47:56 +053034 response->rmcpStatusCode =
35 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
36 return outPayload;
37 }
38
39 std::shared_ptr<session::Session> session;
40 try
41 {
Vernon Maueryae1fda42018-10-15 12:55:34 -070042 session =
43 std::get<session::Manager&>(singletonPool)
44 .getSession(endian::from_ipmi(request->managedSystemSessionID));
Tom Joseph8bb10b72016-12-06 17:47:56 +053045 }
46 catch (std::exception& e)
47 {
Vernon Maueryfc37e592018-12-19 14:55:15 -080048 log<level::ERR>("RAKP12 : session not found",
49 entry("EXCEPTION=%s", e.what()));
Tom Joseph8bb10b72016-12-06 17:47:56 +053050 response->rmcpStatusCode =
51 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
52 return outPayload;
53 }
54
Vernon Mauery9e801a22018-10-12 13:20:49 -070055 auto rakp1Size =
56 sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len);
Tom Joseph56527b92018-03-21 19:31:58 +053057
58 // Validate user name length in the message
59 if (request->user_name_len > userNameMaxLen ||
Vernon Mauery9e801a22018-10-12 13:20:49 -070060 inPayload.size() != rakp1Size)
Tom Joseph56527b92018-03-21 19:31:58 +053061 {
62 response->rmcpStatusCode =
63 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH);
64 return outPayload;
65 }
66
67 session->userName.assign(request->user_name, request->user_name_len);
68
Tom Joseph8bb10b72016-12-06 17:47:56 +053069 // Update transaction time
70 session->updateLastTransactionTime();
71
72 auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
73 auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
74 auto authAlgo = session->getAuthAlgo();
75
76 /*
77 * Generate Key Authentication Code - RAKP 2
78 *
79 * 1) Remote Console Session ID - 4 bytes
80 * 2) Managed System Session ID - 4 bytes
81 * 3) Remote Console Random Number - 16 bytes
82 * 4) Managed System Random Number - 16 bytes
83 * 5) Managed System GUID - 16 bytes
84 * 6) Requested Privilege Level - 1 byte
85 * 7) User Name Length Byte - 1 byte (0 for 'null' username)
86 * 8) User Name - variable (absent for 'null' username)
87 */
88
89 std::vector<uint8_t> input;
90 input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) +
91 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
Vernon Mauery9e801a22018-10-12 13:20:49 -070092 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN +
93 sizeof(request->req_max_privilege_level) +
94 sizeof(request->user_name_len) + session->userName.size());
Tom Joseph8bb10b72016-12-06 17:47:56 +053095
96 auto iter = input.begin();
97
98 // Remote Console Session ID
Vernon Mauery9e801a22018-10-12 13:20:49 -070099 std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
100 iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530101 std::advance(iter, sizeof(rcSessionID));
102
103 // Managed System Session ID
104 std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
105 iter);
106 std::advance(iter, sizeof(bmcSessionID));
107
108 // Copy the Remote Console Random Number from the RAKP1 request to the
109 // Authentication Algorithm
Vernon Mauery9e801a22018-10-12 13:20:49 -0700110 std::copy_n(
111 reinterpret_cast<const uint8_t*>(request->remote_console_random_number),
112 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
113 authAlgo->rcRandomNum.begin());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530114
Vernon Mauery9e801a22018-10-12 13:20:49 -0700115 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530116 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
117
118 // Generate the Managed System Random Number
119 if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700120 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
Tom Joseph8bb10b72016-12-06 17:47:56 +0530121 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN))
122 {
123 response->rmcpStatusCode =
124 static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE);
125 return outPayload;
126 }
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530127 // As stated in Set Session Privilege Level command in IPMI Spec, when
128 // creating a session through Activate command / RAKP 1 message, it must be
129 // established with CALLBACK privilege if requested for callback. All other
130 // sessions are initialy set to USER privilege, regardless of the requested
131 // maximum privilege.
132 session->curPrivLevel = session::Privilege::CALLBACK;
133 if (static_cast<session::Privilege>(request->req_max_privilege_level &
134 session::reqMaxPrivMask) >
135 session::Privilege::CALLBACK)
136 {
137 session->curPrivLevel = session::Privilege::USER;
138 }
Richard Marian Thomaiyar127748a2018-09-06 07:08:51 +0530139 session->reqMaxPrivLevel = request->req_max_privilege_level;
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530140 if (request->user_name_len == 0)
Richard Marian Thomaiyar127748a2018-09-06 07:08:51 +0530141 {
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530142 // Bail out, if user name is not specified.
143 // Yes, NULL user name is not supported for security reasons.
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530144 response->rmcpStatusCode =
145 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
146 return outPayload;
Richard Marian Thomaiyard2563c52018-11-29 11:49:10 +0530147 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530148
149 // Perform user name based lookup
150 std::string userName(request->user_name, request->user_name_len);
151 std::string passwd;
152 uint8_t userId = ipmi::ipmiUserGetUserId(userName);
153 if (userId == ipmi::invalidUserId)
154 {
155 response->rmcpStatusCode =
156 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
157 return outPayload;
158 }
159 // check user is enabled before proceeding.
160 bool userEnabled = false;
161 ipmi::ipmiUserCheckEnabled(userId, userEnabled);
162 if (!userEnabled)
163 {
164 response->rmcpStatusCode =
165 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
166 return outPayload;
167 }
168 // Get the user password for RAKP message authenticate
169 passwd = ipmi::ipmiUserGetPassword(userName);
170 if (passwd.empty())
171 {
172 response->rmcpStatusCode =
173 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
174 return outPayload;
175 }
176 ipmi::PrivAccess userAccess{};
177 ipmi::ChannelAccess chAccess{};
178 // TODO Replace with proper calls.
179 uint8_t chNum = static_cast<uint8_t>(ipmi::EChannelID::chanLan1);
180 // Get channel based access information
181 if ((ipmi::ipmiUserGetPrivilegeAccess(userId, chNum, userAccess) !=
182 IPMI_CC_OK) ||
183 (ipmi::getChannelAccessData(chNum, chAccess) != IPMI_CC_OK))
184 {
185 response->rmcpStatusCode =
186 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
187 return outPayload;
188 }
189 session->chNum = chNum;
Richard Marian Thomaiyard5a4f452019-01-16 12:15:44 +0530190 // minimum privilege of Channel / User / session::privilege::USER/CALLBACK /
191 // has to be used as session current privilege level
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530192 uint8_t minPriv = 0;
193 if (chAccess.privLimit < userAccess.privilege)
194 {
195 minPriv = chAccess.privLimit;
196 }
197 else
198 {
199 minPriv = userAccess.privilege;
200 }
201 if (session->curPrivLevel > static_cast<session::Privilege>(minPriv))
202 {
203 session->curPrivLevel = static_cast<session::Privilege>(minPriv);
204 }
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530205 // For username / privilege lookup, fail with UNAUTH_NAME, if requested
Richard Marian Thomaiyard8e92fe2019-01-16 11:56:23 +0530206 // max privilege does not match user privilege
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530207 if (((request->req_max_privilege_level & userNameOnlyLookupMask) ==
208 userNamePrivLookup) &&
Richard Marian Thomaiyard8e92fe2019-01-16 11:56:23 +0530209 ((request->req_max_privilege_level & session::reqMaxPrivMask) !=
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530210 userAccess.privilege))
211 {
Vernon Maueryfc37e592018-12-19 14:55:15 -0800212 log<level::INFO>(
213 "Username/Privilege lookup failed for requested privilege");
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530214 response->rmcpStatusCode =
215 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
216 return outPayload;
217 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530218
219 std::fill(authAlgo->userKey.data(),
220 authAlgo->userKey.data() + authAlgo->userKey.size(), 0);
221 std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data());
222
Tom Joseph8bb10b72016-12-06 17:47:56 +0530223 // Copy the Managed System Random Number to the Authentication Algorithm
224 std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN,
225 authAlgo->bmcRandomNum.begin());
226 std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
227
228 // Managed System GUID
Tom Joseph83029cb2017-09-01 16:37:31 +0530229 std::copy_n(cache::guid.data(), cache::guid.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530230 std::advance(iter, BMC_GUID_LEN);
231
232 // Requested Privilege Level
Tom Joseph8bb10b72016-12-06 17:47:56 +0530233 std::copy_n(&(request->req_max_privilege_level),
234 sizeof(request->req_max_privilege_level), iter);
235 std::advance(iter, sizeof(request->req_max_privilege_level));
236
Tom Joseph8bb10b72016-12-06 17:47:56 +0530237 // User Name Length Byte
238 std::copy_n(&(request->user_name_len), sizeof(request->user_name_len),
239 iter);
Tom Joseph56527b92018-03-21 19:31:58 +0530240 std::advance(iter, sizeof(request->user_name_len));
241
242 std::copy_n(session->userName.data(), session->userName.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530243
244 // Generate Key Exchange Authentication Code - RAKP2
245 auto output = authAlgo->generateHMAC(input);
246
247 response->messageTag = request->messageTag;
248 response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
249 response->reserved = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700250 response->remoteConsoleSessionID = rcSessionID;
Tom Joseph8bb10b72016-12-06 17:47:56 +0530251
252 // Copy Managed System Random Number to the Response
253 std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
254 response->managed_system_random_number);
255
256 // Copy System GUID to the Response
Vernon Mauery9e801a22018-10-12 13:20:49 -0700257 std::copy_n(cache::guid.data(), cache::guid.size(),
Tom Joseph83029cb2017-09-01 16:37:31 +0530258 response->managed_system_guid);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530259
260 // Insert the HMAC output into the payload
261 outPayload.insert(outPayload.end(), output.begin(), output.end());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530262 return outPayload;
263}
264
265} // namespace command