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