1 #include "rakp34.hpp"
2
3 #include "comm_module.hpp"
4 #include "endian.hpp"
5 #include "guid.hpp"
6 #include "rmcp.hpp"
7 #include "sessions_manager.hpp"
8
9 #include <phosphor-logging/lg2.hpp>
10
11 #include <algorithm>
12 #include <cstring>
13
14 namespace command
15 {
16
applyIntegrityAlgo(const uint32_t bmcSessionID)17 void applyIntegrityAlgo(const uint32_t bmcSessionID)
18 {
19 auto session = session::Manager::get().getSession(bmcSessionID);
20
21 auto authAlgo = session->getAuthAlgo();
22
23 switch (authAlgo->intAlgo)
24 {
25 case cipher::integrity::Algorithms::HMAC_SHA1_96:
26 {
27 session->setIntegrityAlgo(
28 std::make_unique<cipher::integrity::AlgoSHA1>(
29 authAlgo->sessionIntegrityKey));
30 break;
31 }
32 case cipher::integrity::Algorithms::HMAC_SHA256_128:
33 {
34 session->setIntegrityAlgo(
35 std::make_unique<cipher::integrity::AlgoSHA256>(
36 authAlgo->sessionIntegrityKey));
37 break;
38 }
39 default:
40 break;
41 }
42 }
43
applyCryptAlgo(const uint32_t bmcSessionID)44 void applyCryptAlgo(const uint32_t bmcSessionID)
45 {
46 auto session = session::Manager::get().getSession(bmcSessionID);
47
48 auto authAlgo = session->getAuthAlgo();
49
50 switch (authAlgo->cryptAlgo)
51 {
52 case cipher::crypt::Algorithms::AES_CBC_128:
53 {
54 auto intAlgo = session->getIntegrityAlgo();
55 auto k2 = intAlgo->generateKn(authAlgo->sessionIntegrityKey,
56 rmcp::const_2);
57 session->setCryptAlgo(
58 std::make_unique<cipher::crypt::AlgoAES128>(k2));
59 break;
60 }
61 default:
62 break;
63 }
64 }
65
RAKP34(const std::vector<uint8_t> & inPayload,std::shared_ptr<message::Handler> &)66 std::vector<uint8_t> RAKP34(const std::vector<uint8_t>& inPayload,
67 std::shared_ptr<message::Handler>& /* handler */)
68 {
69 std::vector<uint8_t> outPayload(sizeof(RAKP4response));
70 auto request = reinterpret_cast<const RAKP3request*>(inPayload.data());
71 auto response = reinterpret_cast<RAKP4response*>(outPayload.data());
72
73 // Check if the RAKP3 Payload Length is as expected
74 if (inPayload.size() < sizeof(RAKP3request))
75 {
76 lg2::info("RAKP34: Invalid RAKP3 request");
77 response->rmcpStatusCode =
78 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_INTEGRITY_VALUE);
79 return outPayload;
80 }
81
82 // Session ID zero is reserved for Session Setup
83 if (endian::from_ipmi(request->managedSystemSessionID) ==
84 session::sessionZero)
85 {
86 lg2::info("RAKP34: BMC invalid Session ID");
87 response->rmcpStatusCode =
88 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
89 return outPayload;
90 }
91
92 std::shared_ptr<session::Session> session;
93 try
94 {
95 session =
96 session::Manager::get().getSession(request->managedSystemSessionID);
97 }
98 catch (const std::exception& e)
99 {
100 lg2::error("RAKP12 : session not found: {ERROR}", "ERROR", e);
101 response->rmcpStatusCode =
102 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
103 return outPayload;
104 }
105
106 session->updateLastTransactionTime();
107
108 auto authAlgo = session->getAuthAlgo();
109 /*
110 * Key Authentication Code - RAKP 3
111 *
112 * 1) Managed System Random Number - 16 bytes
113 * 2) Remote Console Session ID - 4 bytes
114 * 3) Session Privilege Level - 1 byte
115 * 4) User Name Length Byte - 1 byte (0 for 'null' username)
116 * 5) User Name - variable (absent for 'null' username)
117 */
118
119 // Remote Console Session ID
120 auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
121
122 // Session Privilege Level
123 auto sessPrivLevel = static_cast<uint8_t>(session->reqMaxPrivLevel);
124
125 // User Name Length Byte
126 auto userLength = static_cast<uint8_t>(session->userName.size());
127
128 std::vector<uint8_t> input;
129 input.resize(cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
130 sizeof(rcSessionID) + sizeof(sessPrivLevel) +
131 sizeof(userLength) + userLength);
132
133 auto iter = input.begin();
134
135 // Managed System Random Number
136 std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
137 iter);
138 std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
139
140 // Remote Console Session ID
141 std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
142 iter);
143 std::advance(iter, sizeof(rcSessionID));
144
145 // Session Privilege Level
146 std::copy_n(reinterpret_cast<uint8_t*>(&sessPrivLevel),
147 sizeof(sessPrivLevel), iter);
148 std::advance(iter, sizeof(sessPrivLevel));
149
150 // User Name Length Byte
151 std::copy_n(&userLength, sizeof(userLength), iter);
152 std::advance(iter, sizeof(userLength));
153
154 std::copy_n(session->userName.data(), userLength, iter);
155
156 // Generate Key Exchange Authentication Code - RAKP2
157 auto output = authAlgo->generateHMAC(input);
158
159 if (inPayload.size() != (sizeof(RAKP3request) + output.size()) ||
160 std::memcmp(output.data(), request + 1, output.size()))
161 {
162 lg2::info("Mismatch in HMAC sent by remote console");
163
164 response->messageTag = request->messageTag;
165 response->rmcpStatusCode =
166 static_cast<uint8_t>(RAKP_ReturnCode::INVALID_INTEGRITY_VALUE);
167 response->reserved = 0;
168 response->remoteConsoleSessionID = rcSessionID;
169
170 // close the session
171 session::Manager::get().stopSession(session->getBMCSessionID());
172
173 return outPayload;
174 }
175
176 /*
177 * Session Integrity Key
178 *
179 * 1) Remote Console Random Number - 16 bytes
180 * 2) Managed System Random Number - 16 bytes
181 * 3) Session Privilege Level - 1 byte
182 * 4) User Name Length Byte - 1 byte (0 for 'null' username)
183 * 5) User Name - variable (absent for 'null' username)
184 */
185
186 input.clear();
187
188 input.resize(cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
189 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
190 sizeof(sessPrivLevel) + sizeof(userLength) + userLength);
191 iter = input.begin();
192
193 // Remote Console Random Number
194 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
195 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
196
197 // Managed Console Random Number
198 std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
199 iter);
200 std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
201
202 // Session Privilege Level
203 std::copy_n(reinterpret_cast<uint8_t*>(&sessPrivLevel),
204 sizeof(sessPrivLevel), iter);
205 std::advance(iter, sizeof(sessPrivLevel));
206
207 // User Name Length Byte
208 std::copy_n(&userLength, sizeof(userLength), iter);
209 std::advance(iter, sizeof(userLength));
210
211 std::copy_n(session->userName.data(), userLength, iter);
212
213 // Generate Session Integrity Key
214 auto sikOutput = authAlgo->generateHMAC(input);
215
216 // Update the SIK in the Authentication Algo Interface
217 authAlgo->sessionIntegrityKey.insert(authAlgo->sessionIntegrityKey.begin(),
218 sikOutput.begin(), sikOutput.end());
219
220 /*
221 * Integrity Check Value
222 *
223 * 1) Remote Console Random Number - 16 bytes
224 * 2) Managed System Session ID - 4 bytes
225 * 3) Managed System GUID - 16 bytes
226 */
227
228 // Get Managed System Session ID
229 auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
230
231 input.clear();
232
233 input.resize(cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
234 sizeof(bmcSessionID) + BMC_GUID_LEN);
235 iter = input.begin();
236
237 // Remote Console Random Number
238 std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
239 std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
240
241 // Managed System Session ID
242 std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
243 iter);
244 std::advance(iter, sizeof(bmcSessionID));
245
246 // Managed System GUID
247 const Guid& guid = command::getSystemGUID();
248 std::copy_n(guid.data(), guid.size(), iter);
249
250 // Integrity Check Value
251 auto icv = authAlgo->generateICV(input);
252
253 outPayload.resize(sizeof(RAKP4response));
254
255 response->messageTag = request->messageTag;
256 response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
257 response->reserved = 0;
258 response->remoteConsoleSessionID = rcSessionID;
259
260 // Insert the HMAC output into the payload
261 outPayload.insert(outPayload.end(), icv.begin(), icv.end());
262
263 // Set the Integrity Algorithm
264 applyIntegrityAlgo(session->getBMCSessionID());
265
266 // Set the Confidentiality Algorithm
267 applyCryptAlgo(session->getBMCSessionID());
268
269 session->state(static_cast<uint8_t>(session::State::active));
270 return outPayload;
271 }
272
273 } // namespace command
274