1use core::time;
2use std::{
3 collections::HashMap,
4 net::{ToSocketAddrs, UdpSocket},
5 time::Duration,
6};
7
8use crate::{
9 commands::{
10 AuthVersion, Command, GetChannelAuthCapabilitiesRequest,
11 GetChannelAuthCapabilitiesResponse, GetChannelCipherSuitesRequest,
12 GetChannelCipherSuitesResponse, Privilege,
13 },
14 err::{IPMIClientError, PacketError},
15 helpers::utils::{append_u128_to_vec, append_u32_to_vec, hash_hmac_sha_256},
16 parser::{
17 ipmi_payload::{IpmiPayload, NetFn},
18 ipmi_payload_response::{CompletionCode, IpmiPayloadResponse},
19 ipmi_raw_request::IpmiPayloadRawRequest,
20 rakp::{RAKPMessage1, RAKPMessage2, RAKPMessage3, RAKP},
21 rmcp_open_session::{
22 AuthAlgorithm, ConfidentialityAlgorithm, IntegrityAlgorithm, RMCPPlusOpenSession,
23 RMCPPlusOpenSessionRequest, StatusCode,
24 },
25 AuthType, Packet, Payload, PayloadType,
26 },
27};
28
29pub type Result<T> = core::result::Result<T, IPMIClientError>;
30
31#[derive(Debug)]
32pub struct IPMIClient {
33 client_socket: UdpSocket,
34 auth_state: AuthState,
35 privilege: Privilege,
36 session_seq_number: u32,
37 auth_algorithm: Option<AuthAlgorithm>,
38 integrity_algorithm: Option<IntegrityAlgorithm>,
39 confidentiality_algorithm: Option<ConfidentialityAlgorithm>,
40 managed_system_session_id: Option<u32>,
41 managed_system_random_number: Option<u128>,
42 managed_system_guid: Option<u128>,
43 remote_console_session_id: Option<u32>,
44 remote_console_random_number: u128,
45 username: Option<String>,
46 password_mac_key: Option<Vec<u8>>,
47 channel_auth_capabilities: Option<GetChannelAuthCapabilitiesResponse>,
48 cipher_suite_bytes: Option<Vec<u8>>,
49 cipher_list_index: u8,
50 sik: Option<[u8; 32]>,
51 k1: Option<[u8; 32]>,
52 k2: Option<[u8; 32]>,
53}
54impl IPMIClient {
55 pub fn new<A: ToSocketAddrs>(ipmi_server_addr: A) -> Result<IPMIClient> {
70 let client_socket =
71 UdpSocket::bind("0.0.0.0:0").map_err(|e| IPMIClientError::FailedBind(e))?;
72 let _ = client_socket
73 .set_read_timeout(Some(time::Duration::from_secs(20)))
74 .map_err(|e| IPMIClientError::FailedSetSocketReadTimeout(e));
75 client_socket
76 .connect(ipmi_server_addr)
77 .map_err(|e| IPMIClientError::ConnectToIPMIServer(e))?;
78 Ok(IPMIClient {
79 client_socket,
80 auth_state: AuthState::Discovery,
81 session_seq_number: 0x0000000a,
82 auth_algorithm: None,
83 integrity_algorithm: None,
84 confidentiality_algorithm: None,
85 managed_system_session_id: None,
86 managed_system_guid: None,
87 remote_console_random_number: rand::random::<u128>(),
88 username: None,
89 password_mac_key: None,
90 sik: None,
91 k1: None,
92 k2: None,
93 channel_auth_capabilities: None,
94 cipher_suite_bytes: None,
95 cipher_list_index: 0,
96 managed_system_random_number: None,
97 remote_console_session_id: None,
98 privilege: Privilege::Callback,
99 })
100 }
101
102 pub fn set_read_timeout(&self, duration: Option<Duration>) -> Result<()> {
117 self.client_socket
118 .set_read_timeout(duration)
119 .map_err(|e| IPMIClientError::SetReadTimeOutError(e))
120 }
121
122 pub fn establish_connection<S: ToString>(&mut self, username: S, password: S) -> Result<()> {
143 self.username = Some(username.to_string());
146 let binding = password.to_string();
149 let rakp2_mac_key = binding.as_bytes();
150 self.password_mac_key = Some(rakp2_mac_key.into());
151
152 self.discovery_request()?; self.authenticate()?; Ok(())
155 }
156
157 pub fn send_raw_request<C: Into<Option<Vec<u8>>>, T: TryInto<NetFn>>(
183 &mut self,
184 net_fn: T,
185 command_code: u8,
186 data: C,
187 ) -> Result<IpmiPayloadResponse> {
188 if self.auth_state != AuthState::Established {
190 Err(IPMIClientError::SessionNotEstablishedYet)?
191 };
192
193 if self.privilege != Privilege::Administrator {
195 let set_session = IpmiPayloadRawRequest::new(
196 NetFn::App,
197 (0x3b, 0x6.into()).try_into()?,
198 Some(vec![Privilege::Administrator.into()]),
199 )
200 .create_packet(
201 self.managed_system_session_id.clone().unwrap(),
202 self.session_seq_number,
203 );
204
205 let set_session_response: Packet = self.send_packet(set_session)?;
206 self.session_seq_number += 1;
207 if let Some(Payload::Ipmi(IpmiPayload::Response(payload))) =
208 set_session_response.payload
209 {
210 if payload.completion_code == CompletionCode::CompletedNormally {
211 self.privilege = Privilege::Administrator;
212 }
213 }
214 }
215
216 let raw_request: Packet;
217
218 let netfn: NetFn = net_fn
219 .try_into()
220 .map_err(|_e: <T as TryInto<NetFn>>::Error| {
221 IPMIClientError::NetFnError(crate::err::NetFnError::UnknownNetFn(0))
222 })?;
223 let command: Command = (command_code, netfn)
225 .try_into()
226 .map_err(|e| IPMIClientError::CommandError(e))?;
227
228 match data.into() {
229 None => {
230 raw_request = IpmiPayloadRawRequest::new(netfn, command, None).create_packet(
231 self.managed_system_session_id.clone().unwrap(),
232 self.session_seq_number,
233 );
234 }
235 Some(x) => {
236 raw_request = IpmiPayloadRawRequest::new(netfn, command, Some(x.into()))
237 .create_packet(
238 self.managed_system_session_id.clone().unwrap(),
239 self.session_seq_number,
240 );
241 }
242 }
243
244 let response: Packet = self.send_packet(raw_request)?;
245 self.session_seq_number += 1;
246 if let Some(Payload::Ipmi(IpmiPayload::Response(payload))) = response.payload {
247 return Ok(payload);
248 }
249 Err(IPMIClientError::NoResponse)?
250 }
252
253 fn discovery_request(&mut self) -> Result<()> {
254 let channel_packet =
255 GetChannelAuthCapabilitiesRequest::new(true, 0xE, Privilege::Administrator)
256 .create_packet(AuthType::None, 0x0, 0x0, None);
257 self.send_packet(channel_packet)?;
258
259 let cipher_packet = GetChannelCipherSuitesRequest::default().create_packet();
261 self.send_packet(cipher_packet)?;
262 Ok(())
263 }
264
265 fn authenticate(&mut self) -> Result<()> {
266 let rmcp_open_packet: Packet = RMCPPlusOpenSessionRequest::new(
268 0,
269 Privilege::Administrator,
270 0xa0a2a3a4,
271 self.auth_algorithm.clone().unwrap(),
272 self.integrity_algorithm.clone().unwrap(),
273 self.confidentiality_algorithm.clone().unwrap(),
274 )
275 .into();
276 self.send_packet(rmcp_open_packet)?;
277
278 let rakp1_packet: Packet = RAKPMessage1::new(
280 0x0,
281 self.managed_system_session_id.unwrap(),
282 self.remote_console_random_number,
283 true,
284 Privilege::Administrator,
285 self.username.clone().unwrap(),
286 )
287 .into();
288 self.send_packet(rakp1_packet)?;
289 self.create_session_keys()?;
290
291 let mut rakp3_input_buffer = Vec::new();
293 append_u128_to_vec(
294 &mut rakp3_input_buffer,
295 self.managed_system_random_number.unwrap(),
296 );
297 append_u32_to_vec(
298 &mut rakp3_input_buffer,
299 self.remote_console_session_id.unwrap(),
300 );
301 rakp3_input_buffer.push(0x14);
302 rakp3_input_buffer.push(self.username.clone().unwrap().len().try_into().unwrap());
303 self.username
304 .clone()
305 .unwrap()
306 .as_bytes()
307 .iter()
308 .for_each(|char| rakp3_input_buffer.push(char.clone()));
309
310 let rakp3_auth_code =
311 hash_hmac_sha_256(self.password_mac_key.clone().unwrap(), rakp3_input_buffer);
312 let rakp3_packet: Packet = RAKPMessage3::new(
313 0x0,
314 StatusCode::NoErrors,
315 self.managed_system_session_id.clone().unwrap(),
316 Some(rakp3_auth_code.into()),
317 )
318 .into();
319 self.send_packet(rakp3_packet)?;
320
321 Ok(())
322 }
323
324 fn send_packet(&mut self, request_packet: Packet) -> Result<Packet> {
325 match self.auth_state {
326 AuthState::Established => {
327 self.client_socket
328 .send(
329 &request_packet
330 .to_encrypted_bytes(&self.k1.unwrap(), &self.k2.unwrap())
331 .unwrap(),
332 )
333 .map_err(|e| IPMIClientError::FailedSend(e))?;
334 }
335 _ => {
336 let x: Vec<u8> = request_packet.into();
337 self.client_socket
338 .send(&x)
339 .map_err(|e| IPMIClientError::FailedSend(e))?;
340 }
341 }
342
343 let mut recv_buff = [0; 8092];
344
345 if let Ok((n_bytes, _addr)) = self.client_socket.recv_from(&mut recv_buff) {
346 let response_slice = &recv_buff[..n_bytes];
347 let response_packet: Packet;
348 match self.k2.clone() {
349 None => response_packet = Packet::try_from(response_slice)?,
350 Some(k2) => response_packet = Packet::try_from((response_slice, &k2))?,
351 }
352 if let Some(payload) = response_packet.clone().payload {
353 match payload {
354 Payload::Ipmi(IpmiPayload::Request(_)) => {
355 return Err(IPMIClientError::MisformedResponse)
356 }
357 Payload::Ipmi(IpmiPayload::Response(payload)) => {
358 self.handle_completion_code(payload)?;
359 }
360 Payload::RMCP(RMCPPlusOpenSession::Request(_)) => {
361 return Err(IPMIClientError::MisformedResponse)
362 }
363 Payload::RMCP(RMCPPlusOpenSession::Response(payload)) => self
364 .handle_status_code(Payload::RMCP(RMCPPlusOpenSession::Response(
365 payload,
366 )))?,
367 Payload::RAKP(RAKP::Message2(payload)) => {
368 self.handle_status_code(Payload::RAKP(RAKP::Message2(payload)))?
369 }
370 Payload::RAKP(RAKP::Message4(payload)) => {
371 self.handle_status_code(Payload::RAKP(RAKP::Message4(payload)))?
372 }
373 _ => Err(IPMIClientError::MisformedResponse)?,
374 }
375 return Ok(response_packet);
376 }
377 Ok(response_packet)
378 } else {
379 return Err(IPMIClientError::NoResponse);
380 }
381 }
382
383 fn handle_completion_code(&mut self, payload: IpmiPayloadResponse) -> Result<()> {
385 match payload.completion_code {
386 CompletionCode::CompletedNormally => match payload.command {
387 Command::GetChannelAuthCapabilities => {
388 self.handle_channel_auth_capabilities(payload)?
389 }
390 Command::GetChannelCipherSuites => {
391 while let AuthState::Discovery = self.auth_state {
392 self.cipher_list_index += 1;
393 self.handle_cipher_suites(payload.clone(), self.cipher_list_index)?;
394 }
395 }
396 _ => return Ok(()),
397 },
398 _ => {
399 println!("{:?}", payload.completion_code);
400 return Ok(());
401 }
402 }
403 Ok(())
404 }
405
406 fn handle_status_code(&mut self, payload: Payload) -> Result<()> {
407 if let Payload::RMCP(RMCPPlusOpenSession::Response(response)) = payload.clone() {
408 match response.rmcp_plus_status_code {
409 StatusCode::NoErrors => {
410 self.managed_system_session_id =
411 Some(response.managed_system_session_id.clone());
412 }
413 _ => Err(IPMIClientError::FailedToOpenSession(
414 response.rmcp_plus_status_code,
415 ))?,
416 }
417 }
418
419 if let Payload::RAKP(RAKP::Message2(response)) = payload.clone() {
420 match response.rmcp_plus_status_code {
421 StatusCode::NoErrors => {
422 self.managed_system_guid = Some(response.managed_system_guid);
423 self.remote_console_session_id = Some(response.remote_console_session_id);
424 self.managed_system_random_number = Some(response.managed_system_random_number);
425 self.validate_rakp2(response)?;
427 }
428 _ => Err(IPMIClientError::FailedToOpenSession(
429 response.rmcp_plus_status_code,
430 ))?,
431 }
432 }
433 if let Payload::RAKP(RAKP::Message4(response)) = payload.clone() {
434 match response.rmcp_plus_status_code {
435 StatusCode::NoErrors => {
436 let mut rakp4_input_buffer: Vec<u8> = Vec::new();
438 append_u128_to_vec(
439 &mut rakp4_input_buffer,
440 self.remote_console_random_number.clone(),
441 );
442 append_u32_to_vec(
443 &mut rakp4_input_buffer,
444 self.managed_system_session_id.clone().unwrap(),
445 );
446 append_u128_to_vec(
447 &mut rakp4_input_buffer,
448 self.managed_system_guid.clone().unwrap(),
449 );
450 let auth_code =
451 hash_hmac_sha_256(self.sik.clone().unwrap().into(), rakp4_input_buffer);
452
453 if response.integrity_check_value.clone().unwrap() == auth_code[..16] {
454 self.auth_state = AuthState::Established;
456 } else {
457 Err(IPMIClientError::MismatchedKeyExchangeAuthCode)?
458 }
459 }
460 _ => Err(IPMIClientError::FailedToOpenSession(
461 response.rmcp_plus_status_code,
462 ))?,
463 }
464 }
465
466 Ok(())
467 }
468
469 fn create_session_keys(&mut self) -> Result<()> {
470 let mut sik_input = Vec::new();
471 append_u128_to_vec(&mut sik_input, self.remote_console_random_number);
472 append_u128_to_vec(&mut sik_input, self.managed_system_random_number.unwrap());
473 sik_input.push(0x14);
474 sik_input.push(
475 self.username
476 .clone()
477 .unwrap()
478 .len()
479 .try_into()
480 .map_err(|e| IPMIClientError::UsernameOver255InLength(e))?,
481 );
482 self.username
483 .clone()
484 .unwrap()
485 .as_bytes()
486 .iter()
487 .for_each(|char| sik_input.push(char.clone()));
488
489 self.sik = Some(hash_hmac_sha_256(
490 self.password_mac_key.clone().unwrap(),
491 sik_input,
492 ));
493 self.k1 = Some(hash_hmac_sha_256(self.sik.unwrap().into(), [1; 20].into()));
494 self.k2 = Some(hash_hmac_sha_256(self.sik.unwrap().into(), [2; 20].into()));
495
496 Ok(())
497 }
498
499 fn validate_rakp2(&self, response: RAKPMessage2) -> Result<()> {
500 if let None = response.clone().key_exchange_auth_code {
501 return Ok(());
502 }
503 let mut rakp2_input_buffer: Vec<u8> = Vec::new();
504 append_u32_to_vec(
505 &mut rakp2_input_buffer,
506 self.remote_console_session_id.unwrap(),
507 );
508 append_u32_to_vec(
509 &mut rakp2_input_buffer,
510 self.managed_system_session_id.unwrap(),
511 );
512 append_u128_to_vec(&mut rakp2_input_buffer, self.remote_console_random_number);
513 append_u128_to_vec(
514 &mut rakp2_input_buffer,
515 self.managed_system_random_number.unwrap(),
516 );
517 append_u128_to_vec(&mut rakp2_input_buffer, self.managed_system_guid.unwrap());
518 rakp2_input_buffer.push(0x14);
519 rakp2_input_buffer.push(
520 self.username
521 .clone()
522 .unwrap()
523 .len()
524 .try_into()
525 .map_err(|e| IPMIClientError::UsernameOver255InLength(e))?,
526 );
527 self.username
528 .clone()
529 .unwrap()
530 .as_bytes()
531 .iter()
532 .for_each(|char| rakp2_input_buffer.push(char.clone()));
533
534 let manual_auth_code =
535 hash_hmac_sha_256(self.password_mac_key.clone().unwrap(), rakp2_input_buffer);
536 let mut vec_auth_code = Vec::new();
537 vec_auth_code.extend_from_slice(manual_auth_code.as_slice());
538 if vec_auth_code != response.key_exchange_auth_code.unwrap() {
539 Err(IPMIClientError::FailedToValidateRAKP2)?
540 }
541 Ok(())
542 }
543
544 fn handle_channel_auth_capabilities(&mut self, payload: IpmiPayloadResponse) -> Result<()> {
545 let response: GetChannelAuthCapabilitiesResponse = payload
546 .data
547 .unwrap()
548 .try_into()
549 .map_err(|e| PacketError::IPMIPayload(e))?;
550 if let AuthVersion::IpmiV1_5 = response.auth_version {
552 return Err(IPMIClientError::UnsupportedVersion);
553 }
554 self.channel_auth_capabilities = Some(response);
555 Ok(())
556 }
557
558 fn handle_cipher_suites(
559 &mut self,
560 payload: IpmiPayloadResponse,
561 cipher_list_index: u8,
562 ) -> Result<()> {
563 let response: GetChannelCipherSuitesResponse = payload
564 .data
565 .unwrap()
566 .try_into()
567 .map_err(|e| PacketError::IPMIPayload(e))?;
568 if let Some(mut old_bytes) = self.cipher_suite_bytes.clone() {
570 response
571 .cypher_suite_record_data_bytes
572 .iter()
573 .for_each(|byte| old_bytes.push(*byte));
574 self.cipher_suite_bytes = Some(old_bytes);
575 } else {
576 self.cipher_suite_bytes = Some(response.cypher_suite_record_data_bytes.clone());
577 }
578
579 match response.is_last() {
580 false => {
581 let cipher_packet = GetChannelCipherSuitesRequest::new(
582 0xe,
583 PayloadType::IPMI,
584 true,
585 cipher_list_index,
586 )
587 .create_packet();
588 self.send_packet(cipher_packet)?;
589 Ok(())
590 }
591 true => {
592 self.choose_ciphers();
594
595 self.auth_state = AuthState::Authentication;
597 Ok(())
598 }
599 }
600 }
601
602 fn choose_ciphers(&mut self) {
603 let mut priority_map: HashMap<
604 u8,
605 (
606 u8,
607 (AuthAlgorithm, IntegrityAlgorithm, ConfidentialityAlgorithm),
608 ),
609 > = HashMap::new();
610 if let Some(bytes) = self.cipher_suite_bytes.clone() {
611 bytes.split(|x| *x == 0xc0).for_each(|bytes| {
612 if bytes.len() != 4 {
613 return;
614 }
615 let auth_value: (u8, AuthAlgorithm) = match bytes[1] {
616 0x01 => (2, AuthAlgorithm::RakpHmacSha1),
617 0x02 => (1, AuthAlgorithm::RakpHmacMd5),
618 0x03 => (3, AuthAlgorithm::RakpHmacSha256),
619 _ => (0, AuthAlgorithm::RakpNone),
620 };
621 let integ_value: (u8, IntegrityAlgorithm) = match bytes[2] {
622 0x41 => (2, IntegrityAlgorithm::HmacSha196),
623 0x42 => (3, IntegrityAlgorithm::HmacMd5128),
624 0x43 => (1, IntegrityAlgorithm::Md5128),
625 0x44 => (4, IntegrityAlgorithm::HmacSha256128),
626 _ => (0, IntegrityAlgorithm::None),
627 };
628 let confid_value: (u8, ConfidentialityAlgorithm) = match bytes[3] {
629 0x81 => (3, ConfidentialityAlgorithm::AesCbc128),
630 0x82 => (2, ConfidentialityAlgorithm::XRc4128),
631 0x83 => (1, ConfidentialityAlgorithm::XRc440),
632 _ => (0, ConfidentialityAlgorithm::None),
633 };
634 priority_map.insert(
635 bytes[0],
636 (
637 auth_value.0 + integ_value.0 + confid_value.0,
638 (auth_value.1, integ_value.1, confid_value.1),
639 ),
640 );
641 });
642 let id_to_use = priority_map.iter().max_by_key(|entry| entry.1 .0).unwrap();
643 self.auth_algorithm = Some(id_to_use.1 .1 .0.clone());
644 self.integrity_algorithm = Some(id_to_use.1 .1 .1.clone());
645 self.confidentiality_algorithm = Some(id_to_use.1 .1 .2.clone());
646 } else {
647 self.auth_algorithm = Some(AuthAlgorithm::RakpNone);
648 self.integrity_algorithm = Some(IntegrityAlgorithm::None);
649 self.confidentiality_algorithm = Some(ConfidentialityAlgorithm::None);
650 }
651 }
652}
653
654#[derive(Debug, PartialEq)]
655enum AuthState {
656 Discovery,
657 Authentication,
658 Established,
659}