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