ipmi_rs/connection/impls/rmcp/v2_0/
mod.rs

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        // For good measure, add a compile time assert that
239        // makes sure thread_rng is a crypto rng.
240        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    // TODO: validate session sequence number
448    // TODO: Validate session sequence ID
449    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        // TODO: validate sequence, checksums, etc.
472
473        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            // TODO: need better message here :)
480            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}