ipmi_rs/connection/impls/rmcp/v2_0/messages/
open_session.rs

1use std::num::NonZeroU32;
2
3use crate::app::auth::PrivilegeLevel;
4
5use super::RakpErrorStatusCode;
6
7use crate::connection::rmcp::v2_0::crypto::{
8    AuthenticationAlgorithm, ConfidentialityAlgorithm, IntegrityAlgorithm,
9};
10
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum AlgorithmPayloadError {
13    IncorrectDataLen,
14    IncorrectPayloadLenValue,
15    UnknownAuthAlgorithm(u8),
16    UnknownIntegrityAlgorithm(u8),
17    UnknownConfidentialityAlgorithm(u8),
18    UnknownPayloadType(u8),
19}
20
21#[derive(Debug, Clone, Copy)]
22pub enum AlgorithmPayload {
23    Authentication(AuthenticationAlgorithm),
24    Integrity(IntegrityAlgorithm),
25    Confidentiality(ConfidentialityAlgorithm),
26}
27
28impl AlgorithmPayload {
29    pub fn write(&self, null_byte: bool, buffer: &mut Vec<u8>) {
30        let (ty, value) = match *self {
31            Self::Authentication(a) => (0x00, u8::from(a)),
32            Self::Integrity(i) => (0x01, u8::from(i)),
33            Self::Confidentiality(c) => (0x02, u8::from(c)),
34        };
35
36        // Assert valid value
37        assert!((value & 0xC0) == 0);
38
39        // Type
40        buffer.push(ty);
41
42        // reserved data
43        buffer.extend_from_slice(&[0x00, 0x00]);
44
45        // Payload len OR null byte
46        if null_byte {
47            buffer.push(0x00)
48        } else {
49            buffer.push(0x08);
50        }
51
52        // Authentication algorithm
53        buffer.push(value);
54
55        // Reserved data
56        buffer.extend_from_slice(&[0x00, 0x00, 0x00]);
57    }
58
59    pub fn from_data(data: &[u8]) -> Result<Self, AlgorithmPayloadError> {
60        use AlgorithmPayloadError::*;
61
62        if data.len() != 8 {
63            return Err(IncorrectDataLen);
64        }
65
66        let ty = data[0];
67        let payload_len = data[3];
68
69        if payload_len != 8 {
70            return Err(IncorrectPayloadLenValue);
71        }
72
73        let algo = data[4];
74
75        match ty {
76            0x00 => {
77                let algo = AuthenticationAlgorithm::try_from(algo)
78                    .map_err(|_| UnknownAuthAlgorithm(algo))?;
79                Ok(Self::Authentication(algo))
80            }
81            0x01 => {
82                let algo = IntegrityAlgorithm::try_from(algo)
83                    .map_err(|_| UnknownIntegrityAlgorithm(algo))?;
84                Ok(Self::Integrity(algo))
85            }
86            0x02 => {
87                let algo = ConfidentialityAlgorithm::try_from(algo)
88                    .map_err(|_| UnknownConfidentialityAlgorithm(algo))?;
89                Ok(Self::Confidentiality(algo))
90            }
91            _ => Err(UnknownPayloadType(ty)),
92        }
93    }
94}
95
96#[derive(Debug, Clone)]
97pub struct OpenSessionRequest {
98    pub message_tag: u8,
99    pub requested_max_privilege: Option<PrivilegeLevel>,
100    pub remote_console_session_id: NonZeroU32,
101    // TODO: technically these support vectors of algorithms, but
102    // the testing platform we're trying doesn't seem to support
103    // that very well.
104    pub authentication_algorithms: AuthenticationAlgorithm,
105    pub integrity_algorithms: IntegrityAlgorithm,
106    pub confidentiality_algorithms: ConfidentialityAlgorithm,
107}
108
109impl OpenSessionRequest {
110    pub fn write_data(&self, buffer: &mut Vec<u8>) {
111        buffer.push(self.message_tag);
112        buffer.push(self.requested_max_privilege.map(Into::into).unwrap_or(0));
113
114        // Two reserved bytes
115        buffer.push(0);
116        buffer.push(0);
117
118        buffer.extend_from_slice(&self.remote_console_session_id.get().to_le_bytes());
119
120        AlgorithmPayload::Authentication(self.authentication_algorithms).write(false, buffer);
121        AlgorithmPayload::Integrity(self.integrity_algorithms).write(false, buffer);
122        AlgorithmPayload::Confidentiality(self.confidentiality_algorithms).write(false, buffer);
123    }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq)]
127pub enum ParseSessionResponseError {
128    NotEnoughData,
129    HaveErrorCode(Result<OpenSessionResponseErrorStatusCode, u8>),
130    ZeroRemoteConsoleSessionId,
131    ZeroManagedSystemSessionId,
132    InvalidPrivilegeLevel(u8),
133    InvalidAlgorithmPayload,
134    AuthPayloadWasNonAuthAlgorithm,
135    IntegrityPayloadWasNonIntegrityAlgorithm,
136    ConfidentialityPayloadWasNonConfidentialityAlgorithm,
137    AlgorithmPayloadError(AlgorithmPayloadError),
138}
139
140#[derive(Debug, Clone, PartialEq)]
141pub struct OpenSessionResponse {
142    pub message_tag: u8,
143    pub maximum_privilege_level: PrivilegeLevel,
144    pub remote_console_session_id: NonZeroU32,
145    pub managed_system_session_id: NonZeroU32,
146    pub authentication_payload: AuthenticationAlgorithm,
147    pub integrity_payload: IntegrityAlgorithm,
148    pub confidentiality_payload: ConfidentialityAlgorithm,
149}
150
151impl OpenSessionResponse {
152    pub fn from_data(data: &[u8]) -> Result<Self, ParseSessionResponseError> {
153        use ParseSessionResponseError::*;
154
155        if data.len() < 2 {
156            return Err(NotEnoughData);
157        }
158
159        let message_tag = data[0];
160        let status_code = data[1];
161
162        if status_code != 00 {
163            return Err(HaveErrorCode(
164                OpenSessionResponseErrorStatusCode::try_from(status_code).map_err(|_| status_code),
165            ));
166        }
167
168        if data.len() != 36 {
169            return Err(NotEnoughData);
170        }
171
172        let max_privilege_level =
173            PrivilegeLevel::try_from(data[2]).map_err(|_| InvalidPrivilegeLevel(data[2]))?;
174
175        let remote_console_session_id = u32::from_le_bytes(data[4..8].try_into().unwrap());
176        let remote_console_session_id =
177            NonZeroU32::new(remote_console_session_id).ok_or(ZeroRemoteConsoleSessionId)?;
178        let managed_system_session_id = u32::from_le_bytes(data[8..12].try_into().unwrap());
179        let managed_system_session_id =
180            NonZeroU32::new(managed_system_session_id).ok_or(ZeroManagedSystemSessionId)?;
181
182        let authentication_payload =
183            match AlgorithmPayload::from_data(&data[12..20]).map_err(AlgorithmPayloadError)? {
184                AlgorithmPayload::Authentication(a) => a,
185                _ => return Err(AuthPayloadWasNonAuthAlgorithm),
186            };
187
188        let integrity_payload =
189            match AlgorithmPayload::from_data(&data[20..28]).map_err(AlgorithmPayloadError)? {
190                AlgorithmPayload::Integrity(i) => i,
191                _ => return Err(IntegrityPayloadWasNonIntegrityAlgorithm),
192            };
193
194        let confidentiality_payload =
195            match AlgorithmPayload::from_data(&data[28..36]).map_err(AlgorithmPayloadError)? {
196                AlgorithmPayload::Confidentiality(c) => c,
197                _ => return Err(ConfidentialityPayloadWasNonConfidentialityAlgorithm),
198            };
199
200        Ok(Self {
201            message_tag,
202            maximum_privilege_level: max_privilege_level,
203            remote_console_session_id,
204            managed_system_session_id,
205            authentication_payload,
206            integrity_payload,
207            confidentiality_payload,
208        })
209    }
210}
211
212#[derive(Debug, Clone, Copy, PartialEq)]
213#[repr(u8)]
214pub enum OpenSessionResponseErrorStatusCode {
215    CommonRakp(RakpErrorStatusCode),
216    InvalidPayloadType = 0x03,
217    InvalidAuthenticationAlgorithm = 0x04,
218    InvalidIntegrityAlgorithm = 0x05,
219    InvalidConfidentialityAlgorithm = 0x10,
220    NoMatchingAuthenticationPayload = 0x06,
221    NoMatchingIntegrityPayload = 0x07,
222    NoMatchingCipherSuite = 0x011,
223    InvalidRole = 0x09,
224}
225
226impl TryFrom<u8> for OpenSessionResponseErrorStatusCode {
227    type Error = ();
228
229    fn try_from(value: u8) -> Result<Self, Self::Error> {
230        use OpenSessionResponseErrorStatusCode::*;
231
232        if let Ok(common) = TryFrom::try_from(value) {
233            return Ok(CommonRakp(common));
234        }
235
236        let value = match value {
237            0x03 => InvalidPayloadType,
238            0x04 => InvalidAuthenticationAlgorithm,
239            0x05 => InvalidIntegrityAlgorithm,
240            0x10 => InvalidConfidentialityAlgorithm,
241            0x06 => NoMatchingAuthenticationPayload,
242            0x07 => NoMatchingIntegrityPayload,
243            0x11 => NoMatchingCipherSuite,
244            0x09 => InvalidRole,
245            _ => return Err(()),
246        };
247
248        Ok(value)
249    }
250}
251
252#[test]
253pub fn from_data() {
254    let data = [
255        0x00, 0x00, 0x04, 0x00, 0xa4, 0xa3, 0xa2, 0x0a, 0xe0, 0x34, 0x71, 0x4a, 0x00, 0x00, 0x00,
256        0x08, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
257        0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
258    ];
259
260    let message = OpenSessionResponse::from_data(&data).unwrap();
261
262    let expected = OpenSessionResponse {
263        message_tag: 0x00,
264        maximum_privilege_level: PrivilegeLevel::Administrator,
265        remote_console_session_id: NonZeroU32::new(0x0aa2a3a4).unwrap(),
266        managed_system_session_id: NonZeroU32::new(0x4a7134e0).unwrap(),
267        authentication_payload: AuthenticationAlgorithm::RakpHmacSha1,
268        integrity_payload: IntegrityAlgorithm::HmacSha1_96,
269        confidentiality_payload: ConfidentialityAlgorithm::None,
270    };
271
272    assert_eq!(message, expected);
273}