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 
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 
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 
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