1use std::{num::NonZeroU32, ops::Add};
2
3use crate::{
4 app::auth::PrivilegeLevel,
5 connection::{rmcp::socket::RmcpIpmiSocket, Response},
6};
7
8mod crypto;
9use crypto::CryptoState;
10pub use crypto::{AuthenticationAlgorithm, ConfidentialityAlgorithm, IntegrityAlgorithm};
11
12mod messages;
13pub(super) use messages::*;
14pub use messages::{
15 ParseSessionResponseError, RakpMessage2ErrorStatusCode, RakpMessage2ParseError,
16 RakpMessage4ErrorStatusCode, RakpMessage4ParseError,
17};
18
19use self::crypto::CryptoUnwrapError;
20
21use super::{internal::IpmbState, v1_5, RmcpIpmiError, RmcpIpmiReceiveError, UnwrapSessionError};
22
23#[derive(Debug)]
24pub enum ValidateSessionResponseError {
25 MessageTagMismatch,
26 RemoteConsoleSessionIdMismatch,
27}
28
29#[derive(Debug)]
30pub enum ValidateRakpMessage2Error {
31 MessageTagMismatch,
32 RemoteConsoleSessionIdMismatch,
33}
34
35#[derive(Debug)]
36pub enum ValidateRakpMessage4Error {
37 MessageTagMismatch,
38 ManagedSystemSessionIdMismatch,
39}
40
41#[derive(Debug)]
42pub enum ActivationError {
43 Io(std::io::Error),
44 InvalidKeyExchangeAuthCodeLen(usize, AuthenticationAlgorithm),
45 OpenSessionRequestSend(WriteError),
46 OpenSessionResponseReceive(RmcpIpmiReceiveError),
47 OpenSessionResponseRead(UnwrapSessionError),
48 OpenSessionResponseParse(ParseSessionResponseError),
49 OpenSessionResponseValidate(ValidateSessionResponseError),
50 SendRakpMessage1(WriteError),
51 RakpMessage2Receive(RmcpIpmiReceiveError),
52 RakpMessage2Read(UnwrapSessionError),
53 RakpMessage2Parse(RakpMessage2ParseError),
54 RakpMessage2Validate(ValidateRakpMessage2Error),
55 RakpMessage3Send(WriteError),
56 RakpMessage4Receive(RmcpIpmiReceiveError),
57 RakpMessage4Read(UnwrapSessionError),
58 RakpMessage4Parse(RakpMessage4ParseError),
59 RakpMessage4Validate(ValidateRakpMessage4Error),
60 RakpMessage4InvalidIntegrityCheckValue,
61 ServerAuthenticationFailed,
62}
63
64impl From<ParseSessionResponseError> for ActivationError {
65 fn from(value: ParseSessionResponseError) -> Self {
66 Self::OpenSessionResponseParse(value)
67 }
68}
69
70impl From<ValidateSessionResponseError> for ActivationError {
71 fn from(value: ValidateSessionResponseError) -> Self {
72 Self::OpenSessionResponseValidate(value)
73 }
74}
75
76impl From<ValidateRakpMessage2Error> for ActivationError {
77 fn from(value: ValidateRakpMessage2Error) -> Self {
78 Self::RakpMessage2Validate(value)
79 }
80}
81
82impl From<ValidateRakpMessage4Error> for ActivationError {
83 fn from(value: ValidateRakpMessage4Error) -> Self {
84 Self::RakpMessage4Validate(value)
85 }
86}
87
88#[derive(Debug)]
89pub enum WriteError {
90 Io(std::io::Error),
91 PayloadTooLong,
92 EncryptedPayloadTooLong,
93}
94
95impl From<std::io::Error> for WriteError {
96 fn from(value: std::io::Error) -> Self {
97 Self::Io(value)
98 }
99}
100
101#[derive(Debug, Clone, PartialEq)]
102pub enum ReadError {
103 NotIpmiV2_0,
104 NotEnoughData,
105 NotRmcpPlus,
106 InvalidPayloadType(u8),
107 DecryptionError(CryptoUnwrapError),
108}
109
110impl From<CryptoUnwrapError> for ReadError {
111 fn from(value: CryptoUnwrapError) -> Self {
112 Self::DecryptionError(value)
113 }
114}
115
116#[derive(Debug, Clone, Copy, PartialEq)]
117pub enum PayloadType {
118 IpmiMessage,
119 Sol,
120 RmcpPlusOpenSessionRequest,
121 RmcpPlusOpenSessionResponse,
122 RakpMessage1,
123 RakpMessage2,
124 RakpMessage3,
125 RakpMessage4,
126}
127
128#[derive(Debug, Clone)]
129pub struct Message {
130 pub ty: PayloadType,
131 pub session_id: u32,
132 pub session_sequence_number: u32,
133 pub payload: Vec<u8>,
134}
135
136impl TryFrom<u8> for PayloadType {
137 type Error = ();
138
139 fn try_from(value: u8) -> Result<Self, Self::Error> {
140 let ty = match value {
141 0x00 => PayloadType::IpmiMessage,
142 0x01 => PayloadType::Sol,
143 0x10 => PayloadType::RmcpPlusOpenSessionRequest,
144 0x11 => PayloadType::RmcpPlusOpenSessionResponse,
145 0x12 => PayloadType::RakpMessage1,
146 0x13 => PayloadType::RakpMessage2,
147 0x14 => PayloadType::RakpMessage3,
148 0x15 => PayloadType::RakpMessage4,
149 _ => return Err(()),
150 };
151
152 Ok(ty)
153 }
154}
155
156impl From<PayloadType> for u8 {
157 fn from(value: PayloadType) -> Self {
158 match value {
159 PayloadType::IpmiMessage => 0x00,
160 PayloadType::Sol => 0x01,
161 PayloadType::RmcpPlusOpenSessionRequest => 0x10,
162 PayloadType::RmcpPlusOpenSessionResponse => 0x11,
163 PayloadType::RakpMessage1 => 0x12,
164 PayloadType::RakpMessage2 => 0x13,
165 PayloadType::RakpMessage3 => 0x14,
166 PayloadType::RakpMessage4 => 0x15,
167 }
168 }
169}
170
171#[derive(Debug)]
172pub struct State {
173 socket: RmcpIpmiSocket,
174 session_id: NonZeroU32,
175 session_sequence_number: NonZeroU32,
176 state: CryptoState,
177 ipmb_state: IpmbState,
178}
179
180impl State {
181 fn validate_open_session(
182 req: &OpenSessionRequest,
183 resp: &OpenSessionResponse,
184 ) -> Result<(), ValidateSessionResponseError> {
185 if resp.message_tag != req.message_tag {
186 return Err(ValidateSessionResponseError::MessageTagMismatch);
187 }
188
189 if resp.remote_console_session_id != req.remote_console_session_id {
190 return Err(ValidateSessionResponseError::RemoteConsoleSessionIdMismatch);
191 }
192
193 Ok(())
194 }
195
196 fn validate_rm1_rm2(
197 remote_console_session_id: NonZeroU32,
198 rm1: &RakpMessage1,
199 rm2: &RakpMessage2,
200 ) -> Result<(), ValidateRakpMessage2Error> {
201 if rm1.message_tag != rm2.message_tag {
202 return Err(ValidateRakpMessage2Error::MessageTagMismatch);
203 }
204
205 if remote_console_session_id != rm2.remote_console_session_id {
206 return Err(ValidateRakpMessage2Error::RemoteConsoleSessionIdMismatch);
207 }
208
209 Ok(())
210 }
211
212 fn validate_rm3_rm4(
213 managed_system_session_id: NonZeroU32,
214 rm3: &RakpMessage3,
215 rm4: &RakpMessage4,
216 ) -> Result<(), ValidateRakpMessage4Error> {
217 if rm3.message_tag != rm4.message_tag {
218 return Err(ValidateRakpMessage4Error::MessageTagMismatch);
219 }
220
221 if rm4.managed_system_session_id != managed_system_session_id {
222 return Err(ValidateRakpMessage4Error::ManagedSystemSessionIdMismatch);
223 }
224
225 Ok(())
226 }
227
228 pub fn activate(
229 state: v1_5::State,
230 privilege_level: Option<PrivilegeLevel>,
231 username: &Username,
232 password: &[u8],
233 ) -> Result<Self, ActivationError> {
234 use rand::{CryptoRng, Rng};
235
236 let mut rng = rand::thread_rng();
237
238 fn assert_crypto_rng<T: CryptoRng>(_: &T) {}
241 assert_crypto_rng(&rng);
242
243 fn send(
244 socket: &mut RmcpIpmiSocket,
245 ty: PayloadType,
246 payload: Vec<u8>,
247 ) -> Result<(), WriteError> {
248 let message = Message {
249 ty,
250 session_id: 0,
251 session_sequence_number: 0,
252 payload,
253 };
254
255 socket.send(|buffer| CryptoState::write_unencrypted(&message, buffer))
256 }
257
258 fn recv(data: &mut [u8]) -> Result<Message, UnwrapSessionError> {
259 CryptoState::default()
260 .read_payload(data)
261 .map_err(UnwrapSessionError::V2_0)
262 }
263
264 let mut socket = RmcpIpmiSocket::new(state.release_socket());
265
266 let remote_console_session_id: NonZeroU32 = rng.gen();
267
268 let open_session_request = OpenSessionRequest {
269 message_tag: 0,
270 requested_max_privilege: privilege_level,
271 remote_console_session_id,
272 authentication_algorithms: AuthenticationAlgorithm::RakpHmacSha1,
273 confidentiality_algorithms: ConfidentialityAlgorithm::AesCbc128,
274 integrity_algorithms: IntegrityAlgorithm::HmacSha1_96,
275 };
276
277 log::debug!("Sending RMCP+ Open Session Request. {open_session_request:X?}");
278
279 let mut payload = Vec::new();
280 open_session_request.write_data(&mut payload);
281 send(
282 &mut socket,
283 PayloadType::RmcpPlusOpenSessionRequest,
284 payload,
285 )
286 .map_err(ActivationError::OpenSessionRequestSend)?;
287
288 let data = socket
289 .recv()
290 .map_err(ActivationError::OpenSessionResponseReceive)?;
291
292 let response_data = recv(data).map_err(ActivationError::OpenSessionResponseRead)?;
293
294 let response = match OpenSessionResponse::from_data(&response_data.payload) {
295 Ok(r) => r,
296 Err(ParseSessionResponseError::HaveErrorCode(error_code)) => {
297 log::warn!("RMCP+ error occurred. Status code: '{error_code:?}'");
298 return Err(ParseSessionResponseError::HaveErrorCode(error_code).into());
299 }
300 Err(e) => return Err(e.into()),
301 };
302
303 log::debug!("Received RMCP+ Open Session Response: {response:X?}.");
304
305 Self::validate_open_session(&open_session_request, &response)?;
306
307 let random_data = rng.gen();
308
309 let rm1 = RakpMessage1 {
310 message_tag: 0x0D,
311 managed_system_session_id: response.managed_system_session_id,
312 remote_console_random_number: random_data,
313 requested_maximum_privilege_level: PrivilegeLevel::Administrator,
314 username,
315 };
316
317 let mut payload = Vec::new();
318 rm1.write(&mut payload);
319
320 log::debug!("Sending RMCP+ RAKP Message 1. {rm1:X?}");
321
322 send(&mut socket, PayloadType::RakpMessage1, payload)
323 .map_err(ActivationError::SendRakpMessage1)?;
324
325 let data = socket
326 .recv()
327 .map_err(ActivationError::RakpMessage2Receive)?;
328
329 let v2_message = recv(data).map_err(ActivationError::RakpMessage2Read)?;
330 let rm2 = RakpMessage2::from_data(&v2_message.payload)
331 .map_err(ActivationError::RakpMessage2Parse)?;
332
333 log::debug!("Received RMCP+ RAKP Message 2. {rm2:X?}");
334
335 Self::validate_rm1_rm2(remote_console_session_id, &rm1, &rm2)?;
336
337 let kex_auth_code = rm2.key_exchange_auth_code;
338
339 let required_kex_auth_code_len = match response.authentication_payload {
340 AuthenticationAlgorithm::RakpNone => 0,
341 AuthenticationAlgorithm::RakpHmacSha1 => 20,
342 AuthenticationAlgorithm::RakpHmacSha256 => 32,
343 AuthenticationAlgorithm::RakpHmacMd5 => 16,
344 };
345
346 if kex_auth_code.len() != required_kex_auth_code_len {
347 return Err(ActivationError::InvalidKeyExchangeAuthCodeLen(
348 kex_auth_code.len(),
349 response.authentication_payload,
350 ));
351 }
352
353 let mut crypto_state = CryptoState::new(None, password);
354 let message_3_value = crypto_state.calculate_rakp3_data(&response, &rm1, &rm2);
355
356 let rm3 = if let Some(m3) = message_3_value.as_ref() {
357 RakpMessage3 {
358 message_tag: 0x0A,
359 managed_system_session_id: response.managed_system_session_id,
360 contents: RakpMessage3Contents::Succes(m3),
361 }
362 } else {
363 log::warn!("Received RAKP message 2 with invalid integrity check value.");
364
365 RakpMessage3 {
366 message_tag: 0x0A,
367 managed_system_session_id: response.managed_system_session_id,
368 contents: RakpMessage3Contents::Failure(
369 RakpMessage3ErrorStatusCode::InvalidIntegrityCheckValue,
370 ),
371 }
372 };
373
374 let mut payload = Vec::new();
375 rm3.write(&mut payload);
376
377 log::debug!("Sending RAKP message 3. {rm3:X?}");
378
379 send(&mut socket, PayloadType::RakpMessage3, payload)
380 .map_err(ActivationError::RakpMessage3Send)?;
381
382 if rm3.is_failure() {
383 return Err(ActivationError::ServerAuthenticationFailed)?;
384 }
385
386 let data = socket
387 .recv()
388 .map_err(ActivationError::RakpMessage4Receive)?;
389
390 let message = recv(data).unwrap();
391 let rm4 = RakpMessage4::from_data(&message.payload)
392 .map_err(ActivationError::RakpMessage4Parse)?;
393
394 log::debug!("Received RAKP Message 4: {rm4:X?}");
395
396 Self::validate_rm3_rm4(response.remote_console_session_id, &rm3, &rm4)?;
397
398 if !crypto_state.verify(
399 response.authentication_payload,
400 &rm1.remote_console_random_number,
401 rm3.managed_system_session_id.get(),
402 &rm2.managed_system_guid,
403 rm4.integrity_check_value,
404 ) {
405 log::error!("Received incorrect/invalid integrity check value in RAKP Message 4.");
406 return Err(ActivationError::RakpMessage4InvalidIntegrityCheckValue);
407 }
408
409 let session_id = rm3.managed_system_session_id;
410 let session_sequence_number = NonZeroU32::new(1).unwrap();
411
412 Ok(Self {
413 socket,
414 session_id,
415 session_sequence_number,
416 state: crypto_state,
417 ipmb_state: IpmbState::default(),
418 })
419 }
420
421 pub fn send(&mut self, request: &mut crate::connection::Request) -> Result<(), RmcpIpmiError> {
422 let session_sequence_number = self.session_sequence_number.get();
423
424 if session_sequence_number == u32::MAX {
425 todo!("Handle wrapping session number by re-activating?");
426 }
427
428 self.session_sequence_number =
429 NonZeroU32::new(self.session_sequence_number.get().add(1)).unwrap();
430
431 let payload = super::internal::next_ipmb_message(request, &mut self.ipmb_state);
432
433 let message = Message {
434 ty: PayloadType::IpmiMessage,
435 session_id: self.session_id.get(),
436 session_sequence_number,
437 payload,
438 };
439
440 self.socket
441 .send(|buffer| self.state.write_message(&message, buffer))
442 .unwrap();
443
444 Ok(())
445 }
446
447 pub fn recv(&mut self) -> Result<crate::connection::Response, RmcpIpmiReceiveError> {
450 let data = self.socket.recv()?;
451
452 let data = self
453 .state
454 .read_payload(data)
455 .map_err(|e| RmcpIpmiReceiveError::Session(UnwrapSessionError::V2_0(e)))?
456 .payload;
457
458 if data.len() < 7 {
459 return Err(RmcpIpmiReceiveError::NotEnoughData);
460 }
461
462 let _req_addr = data[0];
463 let netfn = data[1] >> 2;
464 let _checksum1 = data[2];
465 let _rs_addr = data[3];
466 let _rqseq = data[4];
467 let cmd = data[5];
468 let response_data: Vec<_> = data[6..data.len() - 1].to_vec();
469 let _checksum2 = data[data.len() - 1];
470
471 let response = if let Some(resp) = Response::new(
474 crate::connection::Message::new_raw(netfn, cmd, response_data),
475 0,
476 ) {
477 resp
478 } else {
479 return Err(RmcpIpmiReceiveError::EmptyMessage);
481 };
482
483 Ok(response)
484 }
485
486 pub fn send_recv(
487 &mut self,
488 request: &mut crate::connection::Request,
489 ) -> Result<crate::connection::Response, RmcpIpmiError> {
490 self.send(request)?;
491 self.recv().map_err(Into::into)
492 }
493}