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