Skip to main content

ipmi_rs/rmcp/v2_0/messages/
open_session.rs

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