blob: cd7dd689b9a051f174a1e274e63c7c3f5dab52de [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>
13#include <iostream>
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
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 {
31 std::cerr << "RAKP12: BMC invalid Session ID\n";
32 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 {
46 std::cerr << e.what() << "\n";
47 response->rmcpStatusCode =
48 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
49 return outPayload;
50 }
51
Vernon Mauery9e801a22018-10-12 13:20:49 -070052 auto rakp1Size =
53 sizeof(RAKP1request) - (userNameMaxLen - request->user_name_len);
Tom Joseph56527b92018-03-21 19:31:58 +053054
55 // Validate user name length in the message
56 if (request->user_name_len > userNameMaxLen ||
Vernon Mauery9e801a22018-10-12 13:20:49 -070057 inPayload.size() != rakp1Size)
Tom Joseph56527b92018-03-21 19:31:58 +053058 {
59 response->rmcpStatusCode =
60 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_NAME_LENGTH);
61 return outPayload;
62 }
63
64 session->userName.assign(request->user_name, request->user_name_len);
65
Tom Joseph8bb10b72016-12-06 17:47:56 +053066 // Update transaction time
67 session->updateLastTransactionTime();
68
69 auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
70 auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
71 auto authAlgo = session->getAuthAlgo();
72
73 /*
74 * Generate Key Authentication Code - RAKP 2
75 *
76 * 1) Remote Console Session ID - 4 bytes
77 * 2) Managed System Session ID - 4 bytes
78 * 3) Remote Console Random Number - 16 bytes
79 * 4) Managed System Random Number - 16 bytes
80 * 5) Managed System GUID - 16 bytes
81 * 6) Requested Privilege Level - 1 byte
82 * 7) User Name Length Byte - 1 byte (0 for 'null' username)
83 * 8) User Name - variable (absent for 'null' username)
84 */
85
86 std::vector<uint8_t> input;
87 input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) +
88 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
Vernon Mauery9e801a22018-10-12 13:20:49 -070089 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN + BMC_GUID_LEN +
90 sizeof(request->req_max_privilege_level) +
91 sizeof(request->user_name_len) + session->userName.size());
Tom Joseph8bb10b72016-12-06 17:47:56 +053092
93 auto iter = input.begin();
94
95 // Remote Console Session ID
Vernon Mauery9e801a22018-10-12 13:20:49 -070096 std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
97 iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +053098 std::advance(iter, sizeof(rcSessionID));
99
100 // Managed System Session ID
101 std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
102 iter);
103 std::advance(iter, sizeof(bmcSessionID));
104
105 // Copy the Remote Console Random Number from the RAKP1 request to the
106 // Authentication Algorithm
Vernon Mauery9e801a22018-10-12 13:20:49 -0700107 std::copy_n(
108 reinterpret_cast<const uint8_t*>(request->remote_console_random_number),
109 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
110 authAlgo->rcRandomNum.begin());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530111
Vernon Mauery9e801a22018-10-12 13:20:49 -0700112 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530113 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
114
115 // Generate the Managed System Random Number
116 if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) +
Vernon Mauery9e801a22018-10-12 13:20:49 -0700117 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
Tom Joseph8bb10b72016-12-06 17:47:56 +0530118 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN))
119 {
120 response->rmcpStatusCode =
121 static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE);
122 return outPayload;
123 }
124
Richard Marian Thomaiyar127748a2018-09-06 07:08:51 +0530125 session->reqMaxPrivLevel = request->req_max_privilege_level;
126 session->curPrivLevel = static_cast<session::Privilege>(
127 request->req_max_privilege_level & session::reqMaxPrivMask);
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530128 if (request->user_name_len == 0)
Richard Marian Thomaiyar127748a2018-09-06 07:08:51 +0530129 {
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530130 // Bail out, if user name is not specified.
131 // Yes, NULL user name is not supported for security reasons.
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530132 response->rmcpStatusCode =
133 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
134 return outPayload;
Richard Marian Thomaiyard2563c52018-11-29 11:49:10 +0530135 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530136
137 // Perform user name based lookup
138 std::string userName(request->user_name, request->user_name_len);
139 std::string passwd;
140 uint8_t userId = ipmi::ipmiUserGetUserId(userName);
141 if (userId == ipmi::invalidUserId)
142 {
143 response->rmcpStatusCode =
144 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
145 return outPayload;
146 }
147 // check user is enabled before proceeding.
148 bool userEnabled = false;
149 ipmi::ipmiUserCheckEnabled(userId, userEnabled);
150 if (!userEnabled)
151 {
152 response->rmcpStatusCode =
153 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
154 return outPayload;
155 }
156 // Get the user password for RAKP message authenticate
157 passwd = ipmi::ipmiUserGetPassword(userName);
158 if (passwd.empty())
159 {
160 response->rmcpStatusCode =
161 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
162 return outPayload;
163 }
164 ipmi::PrivAccess userAccess{};
165 ipmi::ChannelAccess chAccess{};
166 // TODO Replace with proper calls.
167 uint8_t chNum = static_cast<uint8_t>(ipmi::EChannelID::chanLan1);
168 // Get channel based access information
169 if ((ipmi::ipmiUserGetPrivilegeAccess(userId, chNum, userAccess) !=
170 IPMI_CC_OK) ||
171 (ipmi::getChannelAccessData(chNum, chAccess) != IPMI_CC_OK))
172 {
173 response->rmcpStatusCode =
174 static_cast<uint8_t>(RAKP_ReturnCode::INACTIVE_ROLE);
175 return outPayload;
176 }
177 session->chNum = chNum;
178 // minimum privilege of Channel / User / requested has to be used
179 // as session current privilege level
180 uint8_t minPriv = 0;
181 if (chAccess.privLimit < userAccess.privilege)
182 {
183 minPriv = chAccess.privLimit;
184 }
185 else
186 {
187 minPriv = userAccess.privilege;
188 }
189 if (session->curPrivLevel > static_cast<session::Privilege>(minPriv))
190 {
191 session->curPrivLevel = static_cast<session::Privilege>(minPriv);
192 }
Richard Marian Thomaiyard91fd9d2018-12-06 12:03:50 +0530193 // For username / privilege lookup, fail with UNAUTH_NAME, if requested
194 // max privilege is greater than the user privilege.
195 if (((request->req_max_privilege_level & userNameOnlyLookupMask) ==
196 userNamePrivLookup) &&
197 ((request->req_max_privilege_level & session::reqMaxPrivMask) >
198 userAccess.privilege))
199 {
200 std::cerr
201 << "Username/Privilege lookup failed for requested privilege\n";
202 response->rmcpStatusCode =
203 static_cast<uint8_t>(RAKP_ReturnCode::UNAUTH_NAME);
204 return outPayload;
205 }
Richard Marian Thomaiyar99b87842018-12-06 21:35:43 +0530206
207 std::fill(authAlgo->userKey.data(),
208 authAlgo->userKey.data() + authAlgo->userKey.size(), 0);
209 std::copy_n(passwd.c_str(), passwd.size(), authAlgo->userKey.data());
210
Tom Joseph8bb10b72016-12-06 17:47:56 +0530211 // Copy the Managed System Random Number to the Authentication Algorithm
212 std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN,
213 authAlgo->bmcRandomNum.begin());
214 std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
215
216 // Managed System GUID
Tom Joseph83029cb2017-09-01 16:37:31 +0530217 std::copy_n(cache::guid.data(), cache::guid.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530218 std::advance(iter, BMC_GUID_LEN);
219
220 // Requested Privilege Level
Tom Joseph8bb10b72016-12-06 17:47:56 +0530221 std::copy_n(&(request->req_max_privilege_level),
222 sizeof(request->req_max_privilege_level), iter);
223 std::advance(iter, sizeof(request->req_max_privilege_level));
224
Tom Joseph8bb10b72016-12-06 17:47:56 +0530225 // User Name Length Byte
226 std::copy_n(&(request->user_name_len), sizeof(request->user_name_len),
227 iter);
Tom Joseph56527b92018-03-21 19:31:58 +0530228 std::advance(iter, sizeof(request->user_name_len));
229
230 std::copy_n(session->userName.data(), session->userName.size(), iter);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530231
232 // Generate Key Exchange Authentication Code - RAKP2
233 auto output = authAlgo->generateHMAC(input);
234
235 response->messageTag = request->messageTag;
236 response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
237 response->reserved = 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700238 response->remoteConsoleSessionID = rcSessionID;
Tom Joseph8bb10b72016-12-06 17:47:56 +0530239
240 // Copy Managed System Random Number to the Response
241 std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
242 response->managed_system_random_number);
243
244 // Copy System GUID to the Response
Vernon Mauery9e801a22018-10-12 13:20:49 -0700245 std::copy_n(cache::guid.data(), cache::guid.size(),
Tom Joseph83029cb2017-09-01 16:37:31 +0530246 response->managed_system_guid);
Tom Joseph8bb10b72016-12-06 17:47:56 +0530247
248 // Insert the HMAC output into the payload
249 outPayload.insert(outPayload.end(), output.begin(), output.end());
Tom Joseph8bb10b72016-12-06 17:47:56 +0530250 return outPayload;
251}
252
253} // namespace command