mirror of
https://github.com/Devolutions/IronRDP.git
synced 2025-12-23 12:26:46 +00:00
fix: make license parsing and protocol more resilient (#436)
* Converts `BlobType` to a resilient parsing style * Makes the licensing parsing and handling more resilient to make it compatible with xrdp
This commit is contained in:
parent
a232b4ee0f
commit
5c42ade597
26 changed files with 776 additions and 580 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -4204,7 +4204,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow 0.6.5",
|
||||
"winnow 0.6.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5025,9 +5025,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.5"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8"
|
||||
checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use ironrdp_pdu as pdu;
|
|||
use ironrdp_svc::{StaticChannelSet, SvcServerProcessor};
|
||||
use pdu::rdp::capability_sets::CapabilitySet;
|
||||
use pdu::rdp::headers::ShareControlPdu;
|
||||
use pdu::rdp::server_license::{LicensePdu, LicensingErrorMessage};
|
||||
use pdu::write_buf::WriteBuf;
|
||||
use pdu::{decode, gcc, mcs, nego, rdp};
|
||||
|
||||
|
|
@ -392,7 +393,9 @@ impl Sequence for Acceptor {
|
|||
early_capability,
|
||||
channels,
|
||||
} => {
|
||||
let license = rdp::server_license::InitialServerLicenseMessage::new_status_valid_client_message();
|
||||
let license: LicensePdu = LicensingErrorMessage::new_valid_client()
|
||||
.map_err(ConnectorError::pdu)?
|
||||
.into();
|
||||
|
||||
debug!(message = ?license, "Send");
|
||||
|
||||
|
|
|
|||
|
|
@ -700,13 +700,16 @@ fn create_client_info_pdu(config: &Config, routing_addr: &SocketAddr) -> rdp::Cl
|
|||
};
|
||||
|
||||
// Default flags for all sessions
|
||||
let mut flags = ClientInfoFlags::UNICODE
|
||||
let mut flags = ClientInfoFlags::MOUSE
|
||||
| ClientInfoFlags::MOUSE_HAS_WHEEL
|
||||
| ClientInfoFlags::UNICODE
|
||||
| ClientInfoFlags::DISABLE_CTRL_ALT_DEL
|
||||
| ClientInfoFlags::LOGON_NOTIFY
|
||||
| ClientInfoFlags::LOGON_ERRORS
|
||||
| ClientInfoFlags::NO_AUDIO_PLAYBACK
|
||||
| ClientInfoFlags::VIDEO_DISABLE
|
||||
| ClientInfoFlags::ENABLE_WINDOWS_KEY;
|
||||
| ClientInfoFlags::ENABLE_WINDOWS_KEY
|
||||
| ClientInfoFlags::MAXIMIZE_SHELL;
|
||||
|
||||
if config.autologon {
|
||||
flags |= ClientInfoFlags::AUTOLOGON;
|
||||
|
|
|
|||
|
|
@ -160,34 +160,42 @@ impl Sequence for ConnectionFinalizationSequence {
|
|||
debug!("Server Synchronize");
|
||||
ConnectionFinalizationState::WaitForResponse
|
||||
}
|
||||
ShareDataPdu::Control(control_pdu) => match control_pdu.action {
|
||||
finalization_messages::ControlAction::Cooperate => {
|
||||
if control_pdu.grant_id == 0 && control_pdu.control_id == 0 {
|
||||
debug!("Server Control (Cooperate)");
|
||||
ShareDataPdu::Control(control_pdu) => {
|
||||
match control_pdu.action {
|
||||
finalization_messages::ControlAction::Cooperate => {
|
||||
if control_pdu.grant_id == 0 && control_pdu.control_id == 0 {
|
||||
debug!("Server Control (Cooperate)");
|
||||
} else {
|
||||
warn!(
|
||||
control_pdu.grant_id,
|
||||
control_pdu.control_id,
|
||||
user_channel_id = self.user_channel_id,
|
||||
"Server Control (Cooperate) has non-zero grant_id or control_id",
|
||||
);
|
||||
}
|
||||
ConnectionFinalizationState::WaitForResponse
|
||||
} else {
|
||||
return Err(general_err!("invalid Control Cooperate PDU"));
|
||||
}
|
||||
}
|
||||
finalization_messages::ControlAction::GrantedControl => {
|
||||
debug!(
|
||||
control_pdu.grant_id,
|
||||
control_pdu.control_id,
|
||||
user_channel_id = self.user_channel_id,
|
||||
SERVER_CHANNEL_ID
|
||||
);
|
||||
finalization_messages::ControlAction::GrantedControl => {
|
||||
debug!(
|
||||
control_pdu.grant_id,
|
||||
control_pdu.control_id,
|
||||
user_channel_id = self.user_channel_id,
|
||||
SERVER_CHANNEL_ID
|
||||
);
|
||||
|
||||
if control_pdu.grant_id != self.user_channel_id {
|
||||
warn!("Server Control (Granted Control) had invalid grant_id, expected {}, but got {}", self.user_channel_id, control_pdu.grant_id);
|
||||
}
|
||||
|
||||
if control_pdu.control_id != u32::from(SERVER_CHANNEL_ID) {
|
||||
warn!("Server Control (Granted Control) had invalid control_id, expected {}, but got {}", SERVER_CHANNEL_ID, control_pdu.control_id);
|
||||
}
|
||||
|
||||
if control_pdu.grant_id == self.user_channel_id
|
||||
&& control_pdu.control_id == u32::from(SERVER_CHANNEL_ID)
|
||||
{
|
||||
debug!("Server Control (Granted Control)");
|
||||
ConnectionFinalizationState::WaitForResponse
|
||||
} else {
|
||||
return Err(general_err!("invalid Granted Control PDU"));
|
||||
}
|
||||
_ => return Err(general_err!("unexpected control action")),
|
||||
}
|
||||
_ => return Err(general_err!("unexpected control action")),
|
||||
},
|
||||
}
|
||||
ShareDataPdu::ServerSetErrorInfo(server_error_info::ServerSetErrorInfoPdu(error_info)) => {
|
||||
match error_info {
|
||||
server_error_info::ErrorInfo::ProtocolIndependentCode(
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
use core::fmt;
|
||||
use std::mem;
|
||||
|
||||
use ironrdp_pdu::rdp::server_license;
|
||||
use ironrdp_pdu::rdp::server_license::{self, LicensePdu, ServerLicenseError};
|
||||
use ironrdp_pdu::write_buf::WriteBuf;
|
||||
use ironrdp_pdu::PduHint;
|
||||
use rand_core::{OsRng, RngCore as _};
|
||||
|
||||
use super::legacy;
|
||||
use crate::{
|
||||
encode_send_data_request, ConnectorError, ConnectorResult, ConnectorResultExt as _, Sequence, State, Written,
|
||||
};
|
||||
use crate::{encode_send_data_request, ConnectorResult, ConnectorResultExt as _, Sequence, State, Written};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[non_exhaustive]
|
||||
|
|
@ -98,14 +96,12 @@ impl Sequence for LicenseExchangeSequence {
|
|||
|
||||
LicenseExchangeState::NewLicenseRequest => {
|
||||
let send_data_indication_ctx = legacy::decode_send_data_indication(input)?;
|
||||
let initial_server_license = send_data_indication_ctx
|
||||
.decode_user_data::<server_license::InitialServerLicenseMessage>()
|
||||
.with_context("decode initial server license PDU")?;
|
||||
let license_pdu = send_data_indication_ctx
|
||||
.decode_user_data::<LicensePdu>()
|
||||
.with_context("decode during LicenseExchangeState::NewLicenseRequest")?;
|
||||
|
||||
debug!(message = ?initial_server_license, "Received");
|
||||
|
||||
match initial_server_license.message_type {
|
||||
server_license::InitialMessageType::LicenseRequest(license_request) => {
|
||||
match license_pdu {
|
||||
LicensePdu::ServerLicenseRequest(license_request) => {
|
||||
let mut client_random = [0u8; server_license::RANDOM_NUMBER_SIZE];
|
||||
OsRng.fill_bytes(&mut client_random);
|
||||
|
||||
|
|
@ -123,10 +119,10 @@ impl Sequence for LicenseExchangeSequence {
|
|||
trace!(?encryption_data, "Successfully generated Client New License Request");
|
||||
info!(message = ?new_license_request, "Send");
|
||||
|
||||
let written = encode_send_data_request(
|
||||
let written = encode_send_data_request::<LicensePdu>(
|
||||
send_data_indication_ctx.initiator_id,
|
||||
send_data_indication_ctx.channel_id,
|
||||
&new_license_request,
|
||||
&new_license_request.into(),
|
||||
output,
|
||||
)?;
|
||||
|
||||
|
|
@ -161,21 +157,33 @@ impl Sequence for LicenseExchangeSequence {
|
|||
}
|
||||
}
|
||||
}
|
||||
server_license::InitialMessageType::StatusValidClient(_) => {
|
||||
LicensePdu::LicensingErrorMessage(error_message) => {
|
||||
if error_message.error_code != server_license::LicenseErrorCode::StatusValidClient {
|
||||
return Err(custom_err!(
|
||||
"LicensingErrorMessage",
|
||||
ServerLicenseError::from(error_message)
|
||||
));
|
||||
}
|
||||
info!("Server did not initiate license exchange");
|
||||
(Written::Nothing, LicenseExchangeState::LicenseExchanged)
|
||||
}
|
||||
_ => {
|
||||
return Err(general_err!(
|
||||
"unexpected PDU received during LicenseExchangeState::NewLicenseRequest"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LicenseExchangeState::PlatformChallenge { encryption_data } => {
|
||||
let send_data_indication_ctx = legacy::decode_send_data_indication(input)?;
|
||||
|
||||
match send_data_indication_ctx
|
||||
.decode_user_data::<server_license::ServerPlatformChallenge>()
|
||||
.with_context("decode SERVER_PLATFORM_CHALLENGE")
|
||||
{
|
||||
Ok(challenge) => {
|
||||
let license_pdu = send_data_indication_ctx
|
||||
.decode_user_data::<LicensePdu>()
|
||||
.with_context("decode during LicenseExchangeState::PlatformChallenge")?;
|
||||
|
||||
match license_pdu {
|
||||
LicensePdu::ServerPlatformChallenge(challenge) => {
|
||||
debug!(message = ?challenge, "Received");
|
||||
|
||||
let challenge_response =
|
||||
|
|
@ -188,10 +196,10 @@ impl Sequence for LicenseExchangeSequence {
|
|||
|
||||
debug!(message = ?challenge_response, "Send");
|
||||
|
||||
let written = encode_send_data_request(
|
||||
let written = encode_send_data_request::<LicensePdu>(
|
||||
send_data_indication_ctx.initiator_id,
|
||||
send_data_indication_ctx.channel_id,
|
||||
&challenge_response,
|
||||
&challenge_response.into(),
|
||||
output,
|
||||
)?;
|
||||
|
||||
|
|
@ -200,13 +208,21 @@ impl Sequence for LicenseExchangeSequence {
|
|||
LicenseExchangeState::UpgradeLicense { encryption_data },
|
||||
)
|
||||
}
|
||||
Err(error) => {
|
||||
// FIXME(#269): weird control flow pattern
|
||||
downcast_if_status_valid_client(error, |licensing_error_message| {
|
||||
debug!(message = ?licensing_error_message, "Received");
|
||||
info!("Client licensing completed");
|
||||
(Written::Nothing, LicenseExchangeState::LicenseExchanged)
|
||||
})?
|
||||
LicensePdu::LicensingErrorMessage(error_message) => {
|
||||
if error_message.error_code != server_license::LicenseErrorCode::StatusValidClient {
|
||||
return Err(custom_err!(
|
||||
"LicensingErrorMessage",
|
||||
ServerLicenseError::from(error_message)
|
||||
));
|
||||
}
|
||||
debug!(message = ?error_message, "Received");
|
||||
info!("Client licensing completed");
|
||||
(Written::Nothing, LicenseExchangeState::LicenseExchanged)
|
||||
}
|
||||
_ => {
|
||||
return Err(general_err!(
|
||||
"unexpected PDU received during LicenseExchangeState::PlatformChallenge"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -214,14 +230,12 @@ impl Sequence for LicenseExchangeSequence {
|
|||
LicenseExchangeState::UpgradeLicense { encryption_data } => {
|
||||
let send_data_indication_ctx = legacy::decode_send_data_indication(input)?;
|
||||
|
||||
// FIXME: The ServerUpgradeLicense type is handling both SERVER_UPGRADE_LICENSE and SERVER_NEW_LICENSE PDUs.
|
||||
// It’s expected that fixing #263 will also lead to a better alternative here.
|
||||
let license_pdu = send_data_indication_ctx
|
||||
.decode_user_data::<LicensePdu>()
|
||||
.with_context("decode during SERVER_NEW_LICENSE/LicenseExchangeState::UpgradeLicense")?;
|
||||
|
||||
match send_data_indication_ctx
|
||||
.decode_user_data::<server_license::ServerUpgradeLicense>()
|
||||
.with_context("decode SERVER_NEW_LICENSE/SERVER_UPGRADE_LICENSE")
|
||||
{
|
||||
Ok(upgrade_license) => {
|
||||
match license_pdu {
|
||||
LicensePdu::ServerUpgradeLicense(upgrade_license) => {
|
||||
debug!(message = ?upgrade_license, "Received");
|
||||
|
||||
upgrade_license
|
||||
|
|
@ -230,12 +244,21 @@ impl Sequence for LicenseExchangeSequence {
|
|||
|
||||
debug!("License verified with success");
|
||||
}
|
||||
Err(error) => {
|
||||
// FIXME(#269): weird control flow pattern
|
||||
downcast_if_status_valid_client(error, |licensing_error_message| {
|
||||
debug!(message = ?licensing_error_message, "Received");
|
||||
info!("Client licensing completed");
|
||||
})?;
|
||||
LicensePdu::LicensingErrorMessage(error_message) => {
|
||||
if error_message.error_code != server_license::LicenseErrorCode::StatusValidClient {
|
||||
return Err(custom_err!(
|
||||
"LicensingErrorMessage",
|
||||
ServerLicenseError::from(error_message)
|
||||
));
|
||||
}
|
||||
|
||||
debug!(message = ?error_message, "Received");
|
||||
info!("Client licensing completed");
|
||||
}
|
||||
_ => {
|
||||
return Err(general_err!(
|
||||
"unexpected PDU received during LicenseExchangeState::UpgradeLicense"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,20 +273,3 @@ impl Sequence for LicenseExchangeSequence {
|
|||
Ok(written)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(#269): server_license::ServerLicenseError should not be retrieved from an error type.
|
||||
fn downcast_if_status_valid_client<T, Fn>(error: ConnectorError, op: Fn) -> ConnectorResult<T>
|
||||
where
|
||||
Fn: FnOnce(&server_license::LicensingErrorMessage) -> T,
|
||||
{
|
||||
match std::error::Error::source(&error)
|
||||
.and_then(|source| source.downcast_ref::<server_license::ServerLicenseError>())
|
||||
{
|
||||
Some(server_license::ServerLicenseError::ValidClientStatus(licensing_error_message))
|
||||
if licensing_error_message.error_code == server_license::LicenseErrorCode::StatusValidClient =>
|
||||
{
|
||||
Ok(op(licensing_error_message))
|
||||
}
|
||||
_ => Err(error),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,11 +35,7 @@ pub fn pdu_decode(data: &[u8]) {
|
|||
let _ = decode::<gcc::ConferenceCreateRequest>(data);
|
||||
let _ = decode::<gcc::ConferenceCreateResponse>(data);
|
||||
|
||||
let _ = decode::<server_license::ClientNewLicenseRequest>(data);
|
||||
let _ = decode::<server_license::ClientPlatformChallengeResponse>(data);
|
||||
let _ = decode::<server_license::InitialServerLicenseMessage>(data);
|
||||
let _ = decode::<server_license::ServerLicenseRequest>(data);
|
||||
let _ = decode::<server_license::ServerPlatformChallenge>(data);
|
||||
let _ = decode::<server_license::LicensePdu>(data);
|
||||
|
||||
let _ = decode::<vc::ChannelPduHeader>(data);
|
||||
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ impl<'de> PduDecode<'de> for DemandActive {
|
|||
let _padding = src.read_u16();
|
||||
|
||||
let mut capability_sets = Vec::with_capacity(capability_sets_count);
|
||||
|
||||
for _ in 0..capability_sets_count {
|
||||
capability_sets.push(CapabilitySet::decode(src)?);
|
||||
}
|
||||
|
|
@ -267,12 +268,13 @@ pub enum CapabilitySet {
|
|||
BitmapCodecs(BitmapCodecs),
|
||||
|
||||
// other
|
||||
FrameAcknowledge(FrameAcknowledge),
|
||||
ColorCache(Vec<u8>),
|
||||
DrawNineGridCache(Vec<u8>),
|
||||
DrawGdiPlus(Vec<u8>),
|
||||
Rail(Vec<u8>),
|
||||
WindowList(Vec<u8>),
|
||||
FrameAcknowledge(FrameAcknowledge),
|
||||
BitmapCacheV3(Vec<u8>),
|
||||
}
|
||||
|
||||
impl CapabilitySet {
|
||||
|
|
@ -485,7 +487,8 @@ impl PduEncode for CapabilitySet {
|
|||
| CapabilitySet::DrawNineGridCache(buffer)
|
||||
| CapabilitySet::DrawGdiPlus(buffer)
|
||||
| CapabilitySet::Rail(buffer)
|
||||
| CapabilitySet::WindowList(buffer) => buffer.len(),
|
||||
| CapabilitySet::WindowList(buffer)
|
||||
| CapabilitySet::BitmapCacheV3(buffer) => buffer.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -494,8 +497,13 @@ impl<'de> PduDecode<'de> for CapabilitySet {
|
|||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
ensure_fixed_part_size!(in: src);
|
||||
|
||||
let capability_set_type = CapabilitySetType::from_u16(src.read_u16())
|
||||
.ok_or_else(|| invalid_message_err!("capabilitySetType", "invalid capability set type"))?;
|
||||
let capability_set_type_raw = src.read_u16();
|
||||
let capability_set_type = CapabilitySetType::from_u16(capability_set_type_raw).ok_or_else(|| {
|
||||
unsupported_pdu_err!(
|
||||
"capabilitySetType",
|
||||
format!("invalid capability set type: {}", capability_set_type_raw)
|
||||
)
|
||||
})?;
|
||||
|
||||
let length = src.read_u16() as usize;
|
||||
|
||||
|
|
@ -545,6 +553,7 @@ impl<'de> PduDecode<'de> for CapabilitySet {
|
|||
CapabilitySetType::Rail => Ok(CapabilitySet::Rail(capability_set_buffer.into())),
|
||||
CapabilitySetType::WindowList => Ok(CapabilitySet::WindowList(capability_set_buffer.into())),
|
||||
CapabilitySetType::FrameAcknowledge => Ok(CapabilitySet::FrameAcknowledge(decode(capability_set_buffer)?)),
|
||||
CapabilitySetType::BitmapCacheV3CodecID => Ok(CapabilitySet::BitmapCacheV3(capability_set_buffer.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -556,6 +565,7 @@ enum CapabilitySetType {
|
|||
Order = 0x03,
|
||||
BitmapCache = 0x04,
|
||||
Control = 0x05,
|
||||
BitmapCacheV3CodecID = 0x06,
|
||||
WindowActivation = 0x07,
|
||||
Pointer = 0x08,
|
||||
Share = 0x09,
|
||||
|
|
|
|||
|
|
@ -88,13 +88,11 @@ impl<'de> PduDecode<'de> for Bitmap {
|
|||
let _high_color_flags = src.read_u8();
|
||||
let drawing_flags = BitmapDrawingFlags::from_bits_truncate(src.read_u8());
|
||||
|
||||
let is_multiple_rect_supported = src.read_u16() != 0;
|
||||
if !is_multiple_rect_supported {
|
||||
return Err(invalid_message_err!(
|
||||
"isMultipleRectSupported",
|
||||
"invalid multiple rect support"
|
||||
));
|
||||
}
|
||||
// According to the spec:
|
||||
// "This field MUST be set to TRUE (0x0001) because multiple rectangle support is required for a connection to proceed."
|
||||
// however like FreeRDP, we will ignore this field.
|
||||
// https://github.com/FreeRDP/FreeRDP/blob/ba8cf8cf2158018fb7abbedb51ab245f369be813/libfreerdp/core/capabilities.c#L391
|
||||
let _ = src.read_u16();
|
||||
|
||||
read_padding!(src, 2);
|
||||
|
||||
|
|
|
|||
|
|
@ -322,6 +322,18 @@ pub enum ShareDataPdu {
|
|||
ShutdownDenied,
|
||||
SuppressOutput(SuppressOutputPdu),
|
||||
RefreshRectangle(RefreshRectanglePdu),
|
||||
Update(Vec<u8>),
|
||||
Pointer(Vec<u8>),
|
||||
PlaySound(Vec<u8>),
|
||||
SetKeyboardIndicators(Vec<u8>),
|
||||
BitmapCachePersistentList(Vec<u8>),
|
||||
BitmapCacheErrorPdu(Vec<u8>),
|
||||
SetKeyboardImeStatus(Vec<u8>),
|
||||
OffscreenCacheErrorPdu(Vec<u8>),
|
||||
DrawNineGridErrorPdu(Vec<u8>),
|
||||
DrawGdiPusErrorPdu(Vec<u8>),
|
||||
ArcStatusPdu(Vec<u8>),
|
||||
StatusInfoPdu(Vec<u8>),
|
||||
}
|
||||
|
||||
impl ShareDataPdu {
|
||||
|
|
@ -342,6 +354,18 @@ impl ShareDataPdu {
|
|||
ShareDataPdu::ShutdownDenied => "Shutdown Denied PDU",
|
||||
ShareDataPdu::SuppressOutput(_) => "Suppress Output PDU",
|
||||
ShareDataPdu::RefreshRectangle(_) => "Refresh Rectangle PDU",
|
||||
ShareDataPdu::Update(_) => "Update PDU",
|
||||
ShareDataPdu::Pointer(_) => "Pointer PDU",
|
||||
ShareDataPdu::PlaySound(_) => "Play Sound PDU",
|
||||
ShareDataPdu::SetKeyboardIndicators(_) => "Set Keyboard Indicators PDU",
|
||||
ShareDataPdu::BitmapCachePersistentList(_) => "Bitmap Cache Persistent List PDU",
|
||||
ShareDataPdu::BitmapCacheErrorPdu(_) => "Bitmap Cache Error PDU",
|
||||
ShareDataPdu::SetKeyboardImeStatus(_) => "Set Keyboard IME Status PDU",
|
||||
ShareDataPdu::OffscreenCacheErrorPdu(_) => "Offscreen Cache Error PDU",
|
||||
ShareDataPdu::DrawNineGridErrorPdu(_) => "Draw Nine Grid Error PDU",
|
||||
ShareDataPdu::DrawGdiPusErrorPdu(_) => "Draw GDI PUS Error PDU",
|
||||
ShareDataPdu::ArcStatusPdu(_) => "Arc Status PDU",
|
||||
ShareDataPdu::StatusInfoPdu(_) => "Status Info PDU",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -360,6 +384,18 @@ impl ShareDataPdu {
|
|||
ShareDataPdu::ShutdownDenied => ShareDataPduType::ShutdownDenied,
|
||||
ShareDataPdu::SuppressOutput(_) => ShareDataPduType::SuppressOutput,
|
||||
ShareDataPdu::RefreshRectangle(_) => ShareDataPduType::RefreshRectangle,
|
||||
ShareDataPdu::Update(_) => ShareDataPduType::Update,
|
||||
ShareDataPdu::Pointer(_) => ShareDataPduType::Pointer,
|
||||
ShareDataPdu::PlaySound(_) => ShareDataPduType::PlaySound,
|
||||
ShareDataPdu::SetKeyboardIndicators(_) => ShareDataPduType::SetKeyboardIndicators,
|
||||
ShareDataPdu::BitmapCachePersistentList(_) => ShareDataPduType::BitmapCachePersistentList,
|
||||
ShareDataPdu::BitmapCacheErrorPdu(_) => ShareDataPduType::BitmapCacheErrorPdu,
|
||||
ShareDataPdu::SetKeyboardImeStatus(_) => ShareDataPduType::SetKeyboardImeStatus,
|
||||
ShareDataPdu::OffscreenCacheErrorPdu(_) => ShareDataPduType::OffscreenCacheErrorPdu,
|
||||
ShareDataPdu::DrawNineGridErrorPdu(_) => ShareDataPduType::DrawNineGridErrorPdu,
|
||||
ShareDataPdu::DrawGdiPusErrorPdu(_) => ShareDataPduType::DrawGdiPusErrorPdu,
|
||||
ShareDataPdu::ArcStatusPdu(_) => ShareDataPduType::ArcStatusPdu,
|
||||
ShareDataPdu::StatusInfoPdu(_) => ShareDataPduType::StatusInfoPdu,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -382,18 +418,24 @@ impl ShareDataPdu {
|
|||
ShareDataPduType::ShutdownDenied => Ok(ShareDataPdu::ShutdownDenied),
|
||||
ShareDataPduType::SuppressOutput => Ok(ShareDataPdu::SuppressOutput(SuppressOutputPdu::decode(src)?)),
|
||||
ShareDataPduType::RefreshRectangle => Ok(ShareDataPdu::RefreshRectangle(RefreshRectanglePdu::decode(src)?)),
|
||||
ShareDataPduType::Update
|
||||
| ShareDataPduType::Pointer
|
||||
| ShareDataPduType::PlaySound
|
||||
| ShareDataPduType::SetKeyboardIndicators
|
||||
| ShareDataPduType::BitmapCachePersistentList
|
||||
| ShareDataPduType::BitmapCacheErrorPdu
|
||||
| ShareDataPduType::SetKeyboardImeStatus
|
||||
| ShareDataPduType::OffscreenCacheErrorPdu
|
||||
| ShareDataPduType::DrawNineGridErrorPdu
|
||||
| ShareDataPduType::DrawGdiPusErrorPdu
|
||||
| ShareDataPduType::ArcStatusPdu
|
||||
| ShareDataPduType::StatusInfoPdu => Err(other_err!("unsupported share data PDU")),
|
||||
ShareDataPduType::Update => Ok(ShareDataPdu::Update(src.remaining().to_vec())),
|
||||
ShareDataPduType::Pointer => Ok(ShareDataPdu::Pointer(src.remaining().to_vec())),
|
||||
ShareDataPduType::PlaySound => Ok(ShareDataPdu::PlaySound(src.remaining().to_vec())),
|
||||
ShareDataPduType::SetKeyboardIndicators => {
|
||||
Ok(ShareDataPdu::SetKeyboardIndicators(src.remaining().to_vec()))
|
||||
}
|
||||
ShareDataPduType::BitmapCachePersistentList => {
|
||||
Ok(ShareDataPdu::BitmapCachePersistentList(src.remaining().to_vec()))
|
||||
}
|
||||
ShareDataPduType::BitmapCacheErrorPdu => Ok(ShareDataPdu::BitmapCacheErrorPdu(src.remaining().to_vec())),
|
||||
ShareDataPduType::SetKeyboardImeStatus => Ok(ShareDataPdu::SetKeyboardImeStatus(src.remaining().to_vec())),
|
||||
ShareDataPduType::OffscreenCacheErrorPdu => {
|
||||
Ok(ShareDataPdu::OffscreenCacheErrorPdu(src.remaining().to_vec()))
|
||||
}
|
||||
ShareDataPduType::DrawNineGridErrorPdu => Ok(ShareDataPdu::DrawNineGridErrorPdu(src.remaining().to_vec())),
|
||||
ShareDataPduType::DrawGdiPusErrorPdu => Ok(ShareDataPdu::DrawGdiPusErrorPdu(src.remaining().to_vec())),
|
||||
ShareDataPduType::ArcStatusPdu => Ok(ShareDataPdu::ArcStatusPdu(src.remaining().to_vec())),
|
||||
ShareDataPduType::StatusInfoPdu => Ok(ShareDataPdu::StatusInfoPdu(src.remaining().to_vec())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -412,6 +454,7 @@ impl PduEncode for ShareDataPdu {
|
|||
ShareDataPdu::ShutdownRequest | ShareDataPdu::ShutdownDenied => Ok(()),
|
||||
ShareDataPdu::SuppressOutput(pdu) => pdu.encode(dst),
|
||||
ShareDataPdu::RefreshRectangle(pdu) => pdu.encode(dst),
|
||||
_ => Err(other_err!("Encoding not implemented")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -432,6 +475,18 @@ impl PduEncode for ShareDataPdu {
|
|||
ShareDataPdu::ShutdownRequest | ShareDataPdu::ShutdownDenied => 0,
|
||||
ShareDataPdu::SuppressOutput(pdu) => pdu.size(),
|
||||
ShareDataPdu::RefreshRectangle(pdu) => pdu.size(),
|
||||
ShareDataPdu::Update(buffer)
|
||||
| ShareDataPdu::Pointer(buffer)
|
||||
| ShareDataPdu::PlaySound(buffer)
|
||||
| ShareDataPdu::SetKeyboardIndicators(buffer)
|
||||
| ShareDataPdu::BitmapCachePersistentList(buffer)
|
||||
| ShareDataPdu::BitmapCacheErrorPdu(buffer)
|
||||
| ShareDataPdu::SetKeyboardImeStatus(buffer)
|
||||
| ShareDataPdu::OffscreenCacheErrorPdu(buffer)
|
||||
| ShareDataPdu::DrawNineGridErrorPdu(buffer)
|
||||
| ShareDataPdu::DrawGdiPusErrorPdu(buffer)
|
||||
| ShareDataPdu::ArcStatusPdu(buffer)
|
||||
| ShareDataPdu::StatusInfoPdu(buffer) => buffer.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@ mod server_upgrade_license;
|
|||
pub use self::client_new_license_request::{ClientNewLicenseRequest, PLATFORM_ID};
|
||||
pub use self::client_platform_challenge_response::ClientPlatformChallengeResponse;
|
||||
pub use self::licensing_error_message::{LicenseErrorCode, LicensingErrorMessage, LicensingStateTransition};
|
||||
pub use self::server_license_request::{
|
||||
cert, InitialMessageType, InitialServerLicenseMessage, ProductInfo, Scope, ServerCertificate, ServerLicenseRequest,
|
||||
};
|
||||
pub use self::server_license_request::{cert, ProductInfo, Scope, ServerCertificate, ServerLicenseRequest};
|
||||
pub use self::server_platform_challenge::ServerPlatformChallenge;
|
||||
pub use self::server_upgrade_license::{NewLicenseInformation, ServerUpgradeLicense};
|
||||
|
||||
|
|
@ -155,20 +153,22 @@ pub enum PreambleVersion {
|
|||
V3 = 3, // RDP 5.0, 5.1, 5.2, 6.0, 6.1, 7.0, 7.1, 8.0, 8.1, 10.0, 10.1, 10.2, 10.3, 10.4, and 10.5
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||
pub enum BlobType {
|
||||
Any = 0x00,
|
||||
Data = 0x01,
|
||||
Random = 0x02,
|
||||
Certificate = 0x03,
|
||||
Error = 0x04,
|
||||
RsaKey = 0x06,
|
||||
EncryptedData = 0x09,
|
||||
RsaSignature = 0x08,
|
||||
KeyExchangeAlgorithm = 0x0d,
|
||||
Scope = 0x0e,
|
||||
ClientUserName = 0x0f,
|
||||
ClientMachineNameBlob = 0x10,
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct BlobType(u16);
|
||||
|
||||
impl BlobType {
|
||||
pub const ANY: Self = Self(0x00);
|
||||
pub const DATA: Self = Self(0x01);
|
||||
pub const RANDOM: Self = Self(0x02);
|
||||
pub const CERTIFICATE: Self = Self(0x03);
|
||||
pub const ERROR: Self = Self(0x04);
|
||||
pub const RSA_KEY: Self = Self(0x06);
|
||||
pub const ENCRYPTED_DATA: Self = Self(0x09);
|
||||
pub const RSA_SIGNATURE: Self = Self(0x08);
|
||||
pub const KEY_EXCHANGE_ALGORITHM: Self = Self(0x0d);
|
||||
pub const SCOPE: Self = Self(0x0e);
|
||||
pub const CLIENT_USER_NAME: Self = Self(0x0f);
|
||||
pub const CLIENT_MACHINE_NAME_BLOB: Self = Self(0x10);
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -256,6 +256,12 @@ impl From<PduError> for ServerLicenseError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<LicensingErrorMessage> for ServerLicenseError {
|
||||
fn from(e: LicensingErrorMessage) -> Self {
|
||||
Self::UnexpectedError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl ironrdp_error::legacy::ErrorContext for ServerLicenseError {
|
||||
fn context(&self) -> &'static str {
|
||||
|
|
@ -263,6 +269,7 @@ impl ironrdp_error::legacy::ErrorContext for ServerLicenseError {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct BlobHeader {
|
||||
pub blob_type: BlobType,
|
||||
pub length: usize,
|
||||
|
|
@ -282,7 +289,7 @@ impl PduEncode for BlobHeader {
|
|||
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
ensure_fixed_part_size!(in: dst);
|
||||
|
||||
dst.write_u16(self.blob_type.to_u16().unwrap());
|
||||
dst.write_u16(self.blob_type.0);
|
||||
dst.write_u16(cast_length!("len", self.length)?);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -301,10 +308,7 @@ impl<'de> PduDecode<'de> for BlobHeader {
|
|||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
ensure_fixed_part_size!(in: src);
|
||||
|
||||
let blob_type = src.read_u16();
|
||||
let blob_type =
|
||||
BlobType::from_u16(blob_type).ok_or_else(|| invalid_message_err!("blobType", "invalid blob type"))?;
|
||||
|
||||
let blob_type = BlobType(src.read_u16());
|
||||
let length = cast_length!("len", src.read_u16())?;
|
||||
|
||||
Ok(Self { blob_type, length })
|
||||
|
|
@ -336,38 +340,106 @@ fn compute_mac_data(mac_salt_key: &[u8], data: &[u8]) -> Vec<u8> {
|
|||
md5.finalize().to_vec()
|
||||
}
|
||||
|
||||
fn read_license_header(
|
||||
required_preamble_message_type: PreambleType,
|
||||
src: &mut ReadCursor<'_>,
|
||||
) -> Result<LicenseHeader, PduError> {
|
||||
let license_header = LicenseHeader::decode(src)?;
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum LicensePdu {
|
||||
ClientNewLicenseRequest(ClientNewLicenseRequest),
|
||||
ClientPlatformChallengeResponse(ClientPlatformChallengeResponse),
|
||||
ServerLicenseRequest(ServerLicenseRequest),
|
||||
ServerPlatformChallenge(ServerPlatformChallenge),
|
||||
ServerUpgradeLicense(ServerUpgradeLicense),
|
||||
LicensingErrorMessage(LicensingErrorMessage),
|
||||
}
|
||||
|
||||
// FIXME(#269): ERROR_ALERT licensing packets should not be returned as error by the parser.
|
||||
// Such packets should be handled by the caller, and the caller is responsible for turning
|
||||
// those into "Result::Err" if necessary. It should be possible to decode a `LICENSE_ERROR_MESSAGE`
|
||||
// structure like any other PDU.
|
||||
// Otherwise it requires the caller to match on the error kind in order to check for variants that are
|
||||
// not actual errors, it makes the flow of control harder to write correctly and less obvious.
|
||||
// See `ConnectionConfirm` from the `nego` module for prior art.
|
||||
impl<'de> PduDecode<'de> for LicensePdu {
|
||||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
let license_header = LicenseHeader::decode(src)?;
|
||||
|
||||
if license_header.preamble_message_type != required_preamble_message_type {
|
||||
if license_header.preamble_message_type == PreambleType::ErrorAlert {
|
||||
let license_error = LicensingErrorMessage::decode(src)?;
|
||||
|
||||
if license_error.error_code == LicenseErrorCode::StatusValidClient
|
||||
&& license_error.state_transition == LicensingStateTransition::NoTransition
|
||||
{
|
||||
return Err(invalid_message_err!(
|
||||
"preambleType",
|
||||
"the server has returned STATUS_VALID_CLIENT (not an error)"
|
||||
));
|
||||
} else {
|
||||
return Err(invalid_message_err!("preambleType", "invalid preamble type"));
|
||||
match license_header.preamble_message_type {
|
||||
PreambleType::LicenseRequest => Ok(ServerLicenseRequest::decode(license_header, src)?.into()),
|
||||
PreambleType::PlatformChallenge => Ok(ServerPlatformChallenge::decode(license_header, src)?.into()),
|
||||
PreambleType::NewLicense | PreambleType::UpgradeLicense => {
|
||||
Ok(ServerUpgradeLicense::decode(license_header, src)?.into())
|
||||
}
|
||||
} else {
|
||||
return Err(invalid_message_err!("preambleType", "got unexptected preamble type"));
|
||||
PreambleType::LicenseInfo => Err(unsupported_pdu_err!(
|
||||
"LicensePdu::LicenseInfo",
|
||||
"LicenseInfo is not supported".to_owned()
|
||||
)),
|
||||
PreambleType::NewLicenseRequest => Ok(ClientNewLicenseRequest::decode(license_header, src)?.into()),
|
||||
PreambleType::PlatformChallengeResponse => {
|
||||
Ok(ClientPlatformChallengeResponse::decode(license_header, src)?.into())
|
||||
}
|
||||
PreambleType::ErrorAlert => Ok(LicensingErrorMessage::decode(license_header, src)?.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PduEncode for LicensePdu {
|
||||
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
match self {
|
||||
Self::ClientNewLicenseRequest(ref pdu) => pdu.encode(dst),
|
||||
Self::ClientPlatformChallengeResponse(ref pdu) => pdu.encode(dst),
|
||||
Self::ServerLicenseRequest(ref pdu) => pdu.encode(dst),
|
||||
Self::ServerPlatformChallenge(ref pdu) => pdu.encode(dst),
|
||||
Self::ServerUpgradeLicense(ref pdu) => pdu.encode(dst),
|
||||
Self::LicensingErrorMessage(ref pdu) => pdu.encode(dst),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(license_header)
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ClientNewLicenseRequest(pdu) => pdu.name(),
|
||||
Self::ClientPlatformChallengeResponse(pdu) => pdu.name(),
|
||||
Self::ServerLicenseRequest(pdu) => pdu.name(),
|
||||
Self::ServerPlatformChallenge(pdu) => pdu.name(),
|
||||
Self::ServerUpgradeLicense(pdu) => pdu.name(),
|
||||
Self::LicensingErrorMessage(pdu) => pdu.name(),
|
||||
}
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
match self {
|
||||
Self::ClientNewLicenseRequest(pdu) => pdu.size(),
|
||||
Self::ClientPlatformChallengeResponse(pdu) => pdu.size(),
|
||||
Self::ServerLicenseRequest(pdu) => pdu.size(),
|
||||
Self::ServerPlatformChallenge(pdu) => pdu.size(),
|
||||
Self::ServerUpgradeLicense(pdu) => pdu.size(),
|
||||
Self::LicensingErrorMessage(pdu) => pdu.size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ClientNewLicenseRequest> for LicensePdu {
|
||||
fn from(pdu: ClientNewLicenseRequest) -> Self {
|
||||
Self::ClientNewLicenseRequest(pdu)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ClientPlatformChallengeResponse> for LicensePdu {
|
||||
fn from(pdu: ClientPlatformChallengeResponse) -> Self {
|
||||
Self::ClientPlatformChallengeResponse(pdu)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ServerLicenseRequest> for LicensePdu {
|
||||
fn from(pdu: ServerLicenseRequest) -> Self {
|
||||
Self::ServerLicenseRequest(pdu)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ServerPlatformChallenge> for LicensePdu {
|
||||
fn from(pdu: ServerPlatformChallenge) -> Self {
|
||||
Self::ServerPlatformChallenge(pdu)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ServerUpgradeLicense> for LicensePdu {
|
||||
fn from(pdu: ServerUpgradeLicense) -> Self {
|
||||
Self::ServerUpgradeLicense(pdu)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LicensingErrorMessage> for LicensePdu {
|
||||
fn from(pdu: LicensingErrorMessage) -> Self {
|
||||
Self::LicensingErrorMessage(pdu)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,8 +124,8 @@ impl ClientNewLicenseRequest {
|
|||
}
|
||||
}
|
||||
|
||||
impl PduEncode for ClientNewLicenseRequest {
|
||||
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
impl ClientNewLicenseRequest {
|
||||
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
ensure_size!(in: dst, size: self.size());
|
||||
|
||||
self.license_header.encode(dst)?;
|
||||
|
|
@ -134,18 +134,18 @@ impl PduEncode for ClientNewLicenseRequest {
|
|||
dst.write_u32(PLATFORM_ID);
|
||||
dst.write_slice(&self.client_random);
|
||||
|
||||
BlobHeader::new(BlobType::Random, self.encrypted_premaster_secret.len()).encode(dst)?;
|
||||
BlobHeader::new(BlobType::RANDOM, self.encrypted_premaster_secret.len()).encode(dst)?;
|
||||
dst.write_slice(&self.encrypted_premaster_secret);
|
||||
|
||||
BlobHeader::new(
|
||||
BlobType::ClientUserName,
|
||||
BlobType::CLIENT_USER_NAME,
|
||||
self.client_username.len() + UTF8_NULL_TERMINATOR_SIZE,
|
||||
)
|
||||
.encode(dst)?;
|
||||
utils::write_string_to_cursor(dst, &self.client_username, CharacterSet::Ansi, true)?;
|
||||
|
||||
BlobHeader::new(
|
||||
BlobType::ClientMachineNameBlob,
|
||||
BlobType::CLIENT_MACHINE_NAME_BLOB,
|
||||
self.client_machine_name.len() + UTF8_NULL_TERMINATOR_SIZE,
|
||||
)
|
||||
.encode(dst)?;
|
||||
|
|
@ -154,11 +154,11 @@ impl PduEncode for ClientNewLicenseRequest {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
pub fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
pub fn size(&self) -> usize {
|
||||
self.license_header.size()
|
||||
+ LICENSE_REQUEST_STATIC_FIELDS_SIZE
|
||||
+ RANDOM_NUMBER_SIZE
|
||||
|
|
@ -170,9 +170,8 @@ impl PduEncode for ClientNewLicenseRequest {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'de> PduDecode<'de> for ClientNewLicenseRequest {
|
||||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
let license_header = LicenseHeader::decode(src)?;
|
||||
impl ClientNewLicenseRequest {
|
||||
pub fn decode(license_header: LicenseHeader, src: &mut ReadCursor<'_>) -> PduResult<Self> {
|
||||
if license_header.preamble_message_type != PreambleType::NewLicenseRequest {
|
||||
return Err(invalid_message_err!("preambleMessageType", "unexpected preamble type"));
|
||||
}
|
||||
|
|
@ -190,14 +189,14 @@ impl<'de> PduDecode<'de> for ClientNewLicenseRequest {
|
|||
let client_random = src.read_slice(RANDOM_NUMBER_SIZE).into();
|
||||
|
||||
let premaster_secret_blob_header = BlobHeader::decode(src)?;
|
||||
if premaster_secret_blob_header.blob_type != BlobType::Random {
|
||||
if premaster_secret_blob_header.blob_type != BlobType::RANDOM {
|
||||
return Err(invalid_message_err!("blobType", "invalid blob type"));
|
||||
}
|
||||
ensure_size!(in: src, size: premaster_secret_blob_header.length);
|
||||
let encrypted_premaster_secret = src.read_slice(premaster_secret_blob_header.length).into();
|
||||
|
||||
let username_blob_header = BlobHeader::decode(src)?;
|
||||
if username_blob_header.blob_type != BlobType::ClientUserName {
|
||||
if username_blob_header.blob_type != BlobType::CLIENT_USER_NAME {
|
||||
return Err(invalid_message_err!("blobType", "invalid blob type"));
|
||||
}
|
||||
ensure_size!(in: src, size: username_blob_header.length);
|
||||
|
|
@ -205,7 +204,7 @@ impl<'de> PduDecode<'de> for ClientNewLicenseRequest {
|
|||
utils::decode_string(src.read_slice(username_blob_header.length), CharacterSet::Ansi, false)?;
|
||||
|
||||
let machine_name_blob = BlobHeader::decode(src)?;
|
||||
if machine_name_blob.blob_type != BlobType::ClientMachineNameBlob {
|
||||
if machine_name_blob.blob_type != BlobType::CLIENT_MACHINE_NAME_BLOB {
|
||||
return Err(invalid_message_err!("blobType", "invalid blob type"));
|
||||
}
|
||||
ensure_size!(in: src, size: machine_name_blob.length);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use lazy_static::lazy_static;
|
|||
use super::*;
|
||||
use crate::rdp::server_license::server_license_request::cert::{CertificateType, X509CertificateChain};
|
||||
use crate::rdp::server_license::server_license_request::{ProductInfo, Scope, ServerCertificate};
|
||||
use crate::rdp::server_license::PREAMBLE_SIZE;
|
||||
use crate::rdp::server_license::{LicensePdu, PREAMBLE_SIZE};
|
||||
use crate::{decode, encode_vec};
|
||||
|
||||
const LICENSE_HEADER_BUFFER_NO_SIZE: [u8; 6] = [
|
||||
|
|
@ -60,7 +60,7 @@ const CLIENT_USERNAME: &str = "sample-user";
|
|||
const CLIENT_MACHINE_NAME: &str = "sample-machine-name";
|
||||
|
||||
lazy_static! {
|
||||
pub static ref CLIENT_NEW_LICENSE_REQUEST: ClientNewLicenseRequest = ClientNewLicenseRequest {
|
||||
pub static ref CLIENT_NEW_LICENSE_REQUEST: LicensePdu = ClientNewLicenseRequest {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
|
|
@ -81,7 +81,7 @@ lazy_static! {
|
|||
encrypted_premaster_secret: Vec::from(ENCRYPTED_PREMASTER_SECRET.as_ref()),
|
||||
client_username: CLIENT_USERNAME.to_string(),
|
||||
client_machine_name: CLIENT_MACHINE_NAME.to_string(),
|
||||
};
|
||||
}.into();
|
||||
|
||||
pub static ref REQUEST_BUFFER: Vec<u8> = {
|
||||
let username_len = CLIENT_USERNAME.len() + UTF8_NULL_TERMINATOR_SIZE;
|
||||
|
|
@ -119,146 +119,159 @@ lazy_static! {
|
|||
.concat()
|
||||
};
|
||||
|
||||
pub(crate) static ref SERVER_LICENSE_REQUEST: ServerLicenseRequest = ServerLicenseRequest {
|
||||
server_random: Vec::from(SERVER_RANDOM_BUFFER.as_ref()),
|
||||
product_info: ProductInfo {
|
||||
version: 0x60000,
|
||||
company_name: "Microsoft Corporation".to_string(),
|
||||
product_id: "A02".to_string(),
|
||||
},
|
||||
server_certificate: Some(ServerCertificate {
|
||||
issued_permanently: true,
|
||||
certificate: CertificateType::X509(X509CertificateChain {
|
||||
certificate_array: vec![
|
||||
vec![0x30, 0x82, 0x03, 0xda, 0x30, 0x82, 0x02, 0xc2, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13, 0x7f,
|
||||
0x00, 0x00, 0x01, 0x76, 0x00, 0x8f, 0x08, 0x64, 0x08, 0x68, 0xa7, 0x63, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x76, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
|
||||
0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x50, 0x72, 0x6f, 0x64, 0x32,
|
||||
0x4c, 0x53, 0x52, 0x41, 0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x30, 0x1e, 0x17, 0x0d,
|
||||
0x31, 0x39, 0x31, 0x30, 0x32, 0x36, 0x32, 0x32, 0x35, 0x33, 0x34, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x37,
|
||||
0x30, 0x36, 0x30, 0x36, 0x32, 0x30, 0x34, 0x32, 0x33, 0x38, 0x5a, 0x30, 0x11, 0x31, 0x0f, 0x30, 0x0d,
|
||||
0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x06, 0x42, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x30, 0x82, 0x01, 0x22,
|
||||
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
|
||||
0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa8, 0x6b, 0xda, 0xae, 0x08,
|
||||
0x1d, 0xc5, 0x05, 0x70, 0x7d, 0xa0, 0x41, 0x46, 0xb4, 0x14, 0xcf, 0xfb, 0x8e, 0x09, 0x0b, 0x0a, 0x52,
|
||||
0x8a, 0x7f, 0x7a, 0x35, 0xb6, 0xe3, 0x0d, 0x1c, 0xbe, 0x49, 0x63, 0x41, 0x92, 0x86, 0x00, 0xa2, 0xd3,
|
||||
0xff, 0x5b, 0x08, 0x7d, 0x2b, 0x65, 0xe4, 0xc3, 0x09, 0x68, 0x72, 0x21, 0xc4, 0xd8, 0x0a, 0x21, 0x9e,
|
||||
0x1f, 0xdf, 0xb2, 0xaa, 0x2b, 0x42, 0x68, 0xe7, 0xeb, 0x52, 0xf8, 0x9e, 0xfc, 0x7f, 0x0f, 0x55, 0x26,
|
||||
0x7d, 0x44, 0xfb, 0x35, 0xe5, 0xc2, 0x2c, 0xb6, 0x8d, 0x06, 0xc5, 0xdc, 0xbf, 0x66, 0xf6, 0xb2, 0xf2,
|
||||
0x9b, 0xe2, 0x49, 0xaf, 0xfd, 0x4c, 0x69, 0x46, 0x72, 0xe0, 0x2f, 0x31, 0x77, 0x86, 0x7b, 0x5b, 0x6d,
|
||||
0x49, 0xe6, 0xc7, 0x84, 0xd1, 0xdd, 0x56, 0x89, 0x8d, 0xbd, 0x07, 0x18, 0x01, 0x43, 0x70, 0x9b, 0x00,
|
||||
0x71, 0x16, 0x89, 0x66, 0x2e, 0xb6, 0x5f, 0x62, 0xeb, 0x96, 0xed, 0xf2, 0xdb, 0xdb, 0xcf, 0xdd, 0xa8,
|
||||
0xab, 0xde, 0x93, 0xb3, 0xdb, 0x54, 0xf0, 0x34, 0x4a, 0x28, 0xc3, 0x11, 0xf6, 0xb9, 0xd6, 0x45, 0x3f,
|
||||
0x07, 0xc0, 0x8e, 0x10, 0x7a, 0x2b, 0x56, 0x15, 0xbb, 0x00, 0x9d, 0x82, 0x27, 0xf2, 0x11, 0xa3, 0xda,
|
||||
0x03, 0xaa, 0x51, 0xc0, 0xfd, 0x90, 0xc8, 0x73, 0x81, 0xce, 0x97, 0x30, 0xa2, 0x54, 0x63, 0x6f, 0xfc,
|
||||
0x7f, 0x5b, 0x71, 0xec, 0x11, 0xb0, 0xa0, 0xc8, 0x74, 0x3a, 0xcc, 0x1b, 0x5e, 0xcd, 0x91, 0xa8, 0x18,
|
||||
0x92, 0xeb, 0x33, 0xc4, 0x6d, 0xb8, 0x16, 0x67, 0xe1, 0xc5, 0xa6, 0x26, 0x35, 0x48, 0xc4, 0xe7, 0x94,
|
||||
0xeb, 0xbb, 0xb8, 0xde, 0xd3, 0xe1, 0xc0, 0xcb, 0x00, 0x20, 0xf6, 0xbc, 0xa9, 0xc5, 0x70, 0xc4, 0xda,
|
||||
0x1b, 0x61, 0x0b, 0x9f, 0x0b, 0x19, 0x93, 0xaf, 0x8f, 0x40, 0xbb, 0x26, 0x79, 0x02, 0x03, 0x01, 0x00,
|
||||
0x01, 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01, 0x19, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
|
||||
0x16, 0x04, 0x14, 0xa3, 0xda, 0xe5, 0xef, 0xc3, 0x1c, 0x7a, 0xcf, 0x34, 0x2b, 0xa2, 0x42, 0x2b, 0x77,
|
||||
0xcb, 0x62, 0xfb, 0x4c, 0x28, 0x51, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
|
||||
0x80, 0x14, 0x9c, 0xe1, 0xad, 0x8f, 0xd4, 0x86, 0xd2, 0x1c, 0x7e, 0x48, 0x32, 0xf2, 0x28, 0xfe, 0x87,
|
||||
0x90, 0xe3, 0xb1, 0xc5, 0x8e, 0x30, 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x43, 0x30, 0x41, 0x30,
|
||||
0x3f, 0xa0, 0x3d, 0xa0, 0x3b, 0x86, 0x39, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x2f, 0x2f, 0x2f, 0x2f, 0x52,
|
||||
0x44, 0x32, 0x38, 0x31, 0x38, 0x37, 0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x2f, 0x43, 0x65, 0x72,
|
||||
0x74, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x50, 0x72, 0x6f, 0x64, 0x32, 0x4c, 0x53, 0x52, 0x41,
|
||||
0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x64, 0x06, 0x08,
|
||||
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x58, 0x30, 0x56, 0x30, 0x54, 0x06, 0x08, 0x2b,
|
||||
0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x48, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x2f, 0x2f, 0x2f,
|
||||
0x2f, 0x52, 0x44, 0x32, 0x38, 0x31, 0x38, 0x37, 0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x2f, 0x43,
|
||||
0x65, 0x72, 0x74, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x52, 0x44, 0x32, 0x38, 0x31, 0x38, 0x37,
|
||||
0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x5f, 0x50, 0x72, 0x6f, 0x64, 0x32, 0x4c, 0x53, 0x52, 0x41,
|
||||
0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0c, 0x06, 0x03,
|
||||
0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x17, 0x06, 0x08, 0x2b, 0x06, 0x01,
|
||||
0x04, 0x01, 0x82, 0x37, 0x12, 0x04, 0x0b, 0x16, 0x09, 0x54, 0x4c, 0x53, 0x7e, 0x42, 0x41, 0x53, 0x49,
|
||||
0x43, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
|
||||
0x82, 0x01, 0x01, 0x00, 0x55, 0xd5, 0x94, 0x3b, 0x06, 0xef, 0xf2, 0xb0, 0xf9, 0xd7, 0x36, 0x2a, 0x36,
|
||||
0xe0, 0xf1, 0xd9, 0x18, 0xc1, 0x89, 0x7e, 0xa2, 0xcf, 0x01, 0x6f, 0x22, 0x7b, 0x34, 0x81, 0xf0, 0x7a,
|
||||
0x45, 0x11, 0x6e, 0x75, 0x4b, 0x0b, 0xa8, 0xcd, 0x92, 0x57, 0x19, 0x80, 0xb7, 0x6e, 0x1a, 0x4d, 0x12,
|
||||
0x65, 0x91, 0x56, 0x38, 0x17, 0x22, 0xa2, 0x75, 0xae, 0xf9, 0x12, 0x75, 0x38, 0xf3, 0x19, 0x74, 0xea,
|
||||
0x87, 0x46, 0x1f, 0x98, 0x2c, 0x2f, 0xf9, 0xfc, 0xb4, 0xdc, 0x25, 0xa0, 0xd3, 0x34, 0x1b, 0xbc, 0x21,
|
||||
0xbb, 0x3d, 0x82, 0xad, 0x15, 0xc6, 0x3d, 0x02, 0x75, 0x33, 0x70, 0x25, 0x0a, 0x1a, 0xf7, 0x4c, 0xcb,
|
||||
0x84, 0xa3, 0xc1, 0x78, 0xe6, 0xf5, 0xa1, 0x44, 0x54, 0xc8, 0x34, 0xfd, 0xef, 0xbf, 0x86, 0x81, 0x9d,
|
||||
0x9a, 0x7e, 0xb6, 0xad, 0x71, 0x7e, 0xe4, 0xd9, 0x71, 0x6c, 0xb9, 0xe7, 0xf2, 0xd6, 0xd7, 0xbb, 0x66,
|
||||
0x5a, 0x30, 0xf5, 0x29, 0xae, 0x02, 0x39, 0x3d, 0xea, 0x7a, 0x79, 0x1b, 0x53, 0xc5, 0xbe, 0x8d, 0xfb,
|
||||
0xe2, 0xe4, 0x8e, 0xc2, 0x04, 0xb3, 0x0a, 0x94, 0x75, 0xa3, 0xbf, 0xd4, 0x87, 0xd2, 0x74, 0x15, 0x05,
|
||||
0x5e, 0xd5, 0x8f, 0x94, 0x23, 0x41, 0x13, 0x3f, 0xbd, 0xed, 0x21, 0x55, 0x96, 0xe9, 0xc4, 0x93, 0x34,
|
||||
0x7f, 0xaa, 0xea, 0xe7, 0xb1, 0x9a, 0xca, 0x25, 0x91, 0x18, 0xdf, 0x28, 0x05, 0x8e, 0x53, 0xb3, 0x8c,
|
||||
0x8d, 0xcc, 0xf3, 0xf4, 0x78, 0x76, 0x76, 0x7b, 0x82, 0xd6, 0x75, 0x7a, 0x7d, 0xb3, 0x23, 0x2c, 0xc7,
|
||||
0xbe, 0xa6, 0xb0, 0x50, 0x4d, 0x6c, 0xe2, 0x90, 0x85, 0x97, 0x77, 0x0d, 0x2f, 0xf5, 0x7b, 0xb0, 0xc6,
|
||||
0xad, 0xfa, 0x9a, 0x2c, 0xdf, 0xeb, 0x0d, 0x60, 0xd3, 0x0e, 0xa8, 0x5c, 0x43, 0xab, 0x09, 0x85, 0xa3,
|
||||
0xa9, 0x31, 0x66, 0xbd, 0xe4],
|
||||
vec![0x30, 0x82, 0x04, 0x59, 0x30, 0x82, 0x03, 0x45, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x05, 0x01,
|
||||
0x00, 0x00, 0x00, 0x02, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x30, 0x11,
|
||||
0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x06, 0x42, 0x65, 0x63, 0x6b, 0x65, 0x72,
|
||||
0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x32, 0x36, 0x32, 0x33, 0x32, 0x36, 0x34, 0x35, 0x5a,
|
||||
0x17, 0x0d, 0x33, 0x38, 0x30, 0x31, 0x31, 0x39, 0x30, 0x33, 0x31, 0x34, 0x30, 0x37, 0x5a, 0x30, 0x81,
|
||||
0xa6, 0x31, 0x81, 0xa3, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x20, 0x00, 0x6e, 0x00, 0x63,
|
||||
0x00, 0x61, 0x00, 0x63, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00,
|
||||
0x63, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x32, 0x00, 0x37, 0x30, 0x33, 0x06, 0x03, 0x55, 0x04,
|
||||
0x07, 0x1e, 0x2c, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x69,
|
||||
0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00, 0x63, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x32, 0x00,
|
||||
0x37, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x30, 0x43, 0x06, 0x03,
|
||||
0x55, 0x04, 0x05, 0x1e, 0x3c, 0x00, 0x31, 0x00, 0x42, 0x00, 0x63, 0x00, 0x4b, 0x00, 0x65, 0x00, 0x56,
|
||||
0x00, 0x33, 0x00, 0x4d, 0x00, 0x67, 0x00, 0x74, 0x00, 0x6a, 0x00, 0x55, 0x00, 0x74, 0x00, 0x6f, 0x00,
|
||||
0x32, 0x00, 0x50, 0x00, 0x49, 0x00, 0x68, 0x00, 0x35, 0x00, 0x52, 0x00, 0x57, 0x00, 0x56, 0x00, 0x36,
|
||||
0x00, 0x42, 0x00, 0x58, 0x00, 0x48, 0x00, 0x77, 0x00, 0x3d, 0x00, 0x0d, 0x00, 0x0a, 0x30, 0x58, 0x30,
|
||||
0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x0f, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41,
|
||||
0x00, 0xab, 0xac, 0x87, 0x11, 0x83, 0xbf, 0xe9, 0x48, 0x25, 0x00, 0x2c, 0x33, 0x31, 0x5e, 0x3d, 0x78,
|
||||
0xc8, 0x5f, 0x82, 0xcb, 0x36, 0x41, 0xf5, 0xb4, 0x65, 0x15, 0xee, 0x04, 0x31, 0xae, 0xe2, 0x48, 0x58,
|
||||
0x99, 0x7f, 0x4f, 0x90, 0x1d, 0xf7, 0x7c, 0xd7, 0xf8, 0x47, 0x93, 0xa0, 0xca, 0x9c, 0xdf, 0x91, 0xb0,
|
||||
0x41, 0xe8, 0x05, 0x4b, 0xdc, 0x24, 0x5b, 0x72, 0xf7, 0x68, 0x91, 0x84, 0xfb, 0x19, 0x02, 0x03, 0x01,
|
||||
0x00, 0x01, 0xa3, 0x82, 0x01, 0xf4, 0x30, 0x82, 0x01, 0xf0, 0x30, 0x14, 0x06, 0x09, 0x2b, 0x06, 0x01,
|
||||
0x04, 0x01, 0x82, 0x37, 0x12, 0x04, 0x01, 0x01, 0xff, 0x04, 0x04, 0x01, 0x00, 0x05, 0x00, 0x30, 0x3c,
|
||||
0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x02, 0x01, 0x01, 0xff, 0x04, 0x2c, 0x4d,
|
||||
0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00,
|
||||
0x20, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74,
|
||||
0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x30, 0x81, 0xdd, 0x06, 0x09, 0x2b, 0x06, 0x01,
|
||||
0x04, 0x01, 0x82, 0x37, 0x12, 0x05, 0x01, 0x01, 0xff, 0x04, 0x81, 0xcc, 0x00, 0x30, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x22, 0x04, 0x00, 0x00, 0x1c, 0x00, 0x4a, 0x00, 0x66, 0x00,
|
||||
0x4a, 0x00, 0xb0, 0x00, 0x03, 0x00, 0x33, 0x00, 0x64, 0x00, 0x32, 0x00, 0x36, 0x00, 0x37, 0x00, 0x39,
|
||||
0x00, 0x35, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x65, 0x00, 0x65, 0x00, 0x62, 0x00, 0x37, 0x00, 0x2d, 0x00,
|
||||
0x31, 0x00, 0x31, 0x00, 0x64, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x62, 0x00, 0x39, 0x00, 0x34, 0x00, 0x65,
|
||||
0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x63, 0x00, 0x30, 0x00, 0x34, 0x00, 0x66, 0x00, 0x61, 0x00,
|
||||
0x33, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00, 0x64, 0x00, 0x00, 0x00, 0x33, 0x00, 0x64, 0x00, 0x32,
|
||||
0x00, 0x36, 0x00, 0x37, 0x00, 0x39, 0x00, 0x35, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x65, 0x00, 0x65, 0x00,
|
||||
0x62, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x31, 0x00, 0x64, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x62,
|
||||
0x00, 0x39, 0x00, 0x34, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x63, 0x00, 0x30, 0x00,
|
||||
0x34, 0x00, 0x66, 0x00, 0x61, 0x00, 0x33, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00, 0x64, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x80, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x06, 0x09,
|
||||
0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x06, 0x01, 0x01, 0xff, 0x04, 0x70, 0x00, 0x30, 0x00,
|
||||
0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x34, 0x00,
|
||||
0x4c, 0x00, 0x34, 0x00, 0x4c, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x43, 0x00, 0x53,
|
||||
0x00, 0x51, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x34, 0x00, 0x32, 0x00, 0x39, 0x00, 0x2d, 0x00,
|
||||
0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x33, 0x00, 0x34, 0x00, 0x39,
|
||||
0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x41, 0x00, 0x54, 0x00, 0x33, 0x00, 0x35, 0x00, 0x33, 0x00,
|
||||
0x00, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x55,
|
||||
0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x37, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x01, 0x01, 0xff,
|
||||
0x04, 0x2d, 0x30, 0x2b, 0xa1, 0x22, 0xa4, 0x20, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x34,
|
||||
0x00, 0x4c, 0x00, 0x34, 0x00, 0x4c, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x43, 0x00,
|
||||
0x53, 0x00, 0x51, 0x00, 0x00, 0x00, 0x82, 0x05, 0x01, 0x00, 0x00, 0x00, 0x02, 0x30, 0x09, 0x06, 0x05,
|
||||
0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3e, 0xd3, 0xd5, 0x61, 0x8a,
|
||||
0x87, 0x7b, 0x98, 0x2c, 0x6d, 0x20, 0x38, 0x12, 0x08, 0xd8, 0xf7, 0x83, 0x08, 0xf8, 0xe6, 0xb2, 0xe1,
|
||||
0x21, 0xe1, 0x30, 0x61, 0x12, 0x19, 0xe8, 0xc1, 0x41, 0xaf, 0x59, 0x7c, 0x1e, 0x3e, 0xc8, 0x40, 0x9e,
|
||||
0x24, 0xe8, 0x8d, 0x0c, 0x41, 0xfd, 0xf8, 0x3e, 0xa1, 0xb3, 0xac, 0x56, 0xac, 0x52, 0x91, 0x5a, 0xf8,
|
||||
0xd0, 0x40, 0x8e, 0x13, 0x47, 0xa9, 0x8a, 0x0a, 0x62, 0x6d, 0x11, 0x89, 0x20, 0x56, 0xe7, 0xd6, 0x5f,
|
||||
0x12, 0x44, 0x94, 0xbf, 0x63, 0x99, 0xa3, 0x42, 0x40, 0xd5, 0xc6, 0x8c, 0x1f, 0x4b, 0xf8, 0xaf, 0x83,
|
||||
0x8e, 0xf6, 0x74, 0xb2, 0x0b, 0x55, 0x13, 0x4a, 0x76, 0xed, 0x37, 0xd8, 0x3d, 0x13, 0xe7, 0xae, 0x43,
|
||||
0x4c, 0x9a, 0x61, 0x6c, 0x7b, 0x1b, 0xd1, 0xaa, 0x00, 0x97, 0xdf, 0x5b, 0x85, 0x9f, 0xc8, 0xee, 0x6c,
|
||||
0xe5, 0xa2, 0x63, 0x76, 0xe4, 0x06, 0xd3, 0x2a, 0xe0, 0x55, 0xe1, 0x92, 0x78, 0xed, 0x03, 0x7b, 0x7d,
|
||||
0x1a, 0x6e, 0xc2, 0x56, 0xdc, 0xad, 0x6e, 0xd7, 0xa9, 0xfe, 0xa7, 0xfd, 0x09, 0x0a, 0xa6, 0xd5, 0x8a,
|
||||
0x99, 0xa4, 0x75, 0x89, 0xad, 0x84, 0xc7, 0x09, 0xf7, 0x4c, 0x6e, 0xd0, 0xe2, 0x80, 0x17, 0x62, 0xfa,
|
||||
0x86, 0xfe, 0x43, 0x51, 0xf2, 0xb4, 0xf6, 0xef, 0x3b, 0xb3, 0x3d, 0x1f, 0xef, 0xa3, 0xcb, 0xa2, 0x57,
|
||||
0x25, 0x7c, 0x02, 0xf2, 0x27, 0x1c, 0x87, 0x70, 0x8e, 0x84, 0x20, 0xfe, 0x1d, 0x4a, 0xc4, 0x87, 0x24,
|
||||
0x3b, 0xba, 0xff, 0x34, 0x1a, 0xe2, 0xff, 0xa2, 0x43, 0x39, 0xd8, 0x19, 0x97, 0xf8, 0xf0, 0xf9, 0x73,
|
||||
0xa6, 0xb6, 0x55, 0x64, 0xa6, 0xca, 0xa3, 0x48, 0x22, 0xb7, 0x1a, 0x9b, 0x98, 0x1a, 0x8e, 0x2f, 0xaa,
|
||||
0xec, 0xc1, 0xfe, 0x25, 0x36, 0x2b, 0x70, 0x97, 0x8c, 0x5b, 0x62, 0x21, 0xc3],
|
||||
],
|
||||
pub(crate) static ref SERVER_LICENSE_REQUEST: LicensePdu = {
|
||||
let mut req = ServerLicenseRequest {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
},
|
||||
preamble_message_type: PreambleType::LicenseRequest,
|
||||
preamble_flags: PreambleFlags::empty(),
|
||||
preamble_version: PreambleVersion::V3,
|
||||
preamble_message_size: 0,
|
||||
},
|
||||
server_random: Vec::from(SERVER_RANDOM_BUFFER.as_ref()),
|
||||
product_info: ProductInfo {
|
||||
version: 0x60000,
|
||||
company_name: "Microsoft Corporation".to_string(),
|
||||
product_id: "A02".to_string(),
|
||||
},
|
||||
server_certificate: Some(ServerCertificate {
|
||||
issued_permanently: true,
|
||||
certificate: CertificateType::X509(X509CertificateChain {
|
||||
certificate_array: vec![
|
||||
vec![0x30, 0x82, 0x03, 0xda, 0x30, 0x82, 0x02, 0xc2, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13, 0x7f,
|
||||
0x00, 0x00, 0x01, 0x76, 0x00, 0x8f, 0x08, 0x64, 0x08, 0x68, 0xa7, 0x63, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x76, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
|
||||
0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x50, 0x72, 0x6f, 0x64, 0x32,
|
||||
0x4c, 0x53, 0x52, 0x41, 0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x30, 0x1e, 0x17, 0x0d,
|
||||
0x31, 0x39, 0x31, 0x30, 0x32, 0x36, 0x32, 0x32, 0x35, 0x33, 0x34, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x37,
|
||||
0x30, 0x36, 0x30, 0x36, 0x32, 0x30, 0x34, 0x32, 0x33, 0x38, 0x5a, 0x30, 0x11, 0x31, 0x0f, 0x30, 0x0d,
|
||||
0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x06, 0x42, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x30, 0x82, 0x01, 0x22,
|
||||
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
|
||||
0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa8, 0x6b, 0xda, 0xae, 0x08,
|
||||
0x1d, 0xc5, 0x05, 0x70, 0x7d, 0xa0, 0x41, 0x46, 0xb4, 0x14, 0xcf, 0xfb, 0x8e, 0x09, 0x0b, 0x0a, 0x52,
|
||||
0x8a, 0x7f, 0x7a, 0x35, 0xb6, 0xe3, 0x0d, 0x1c, 0xbe, 0x49, 0x63, 0x41, 0x92, 0x86, 0x00, 0xa2, 0xd3,
|
||||
0xff, 0x5b, 0x08, 0x7d, 0x2b, 0x65, 0xe4, 0xc3, 0x09, 0x68, 0x72, 0x21, 0xc4, 0xd8, 0x0a, 0x21, 0x9e,
|
||||
0x1f, 0xdf, 0xb2, 0xaa, 0x2b, 0x42, 0x68, 0xe7, 0xeb, 0x52, 0xf8, 0x9e, 0xfc, 0x7f, 0x0f, 0x55, 0x26,
|
||||
0x7d, 0x44, 0xfb, 0x35, 0xe5, 0xc2, 0x2c, 0xb6, 0x8d, 0x06, 0xc5, 0xdc, 0xbf, 0x66, 0xf6, 0xb2, 0xf2,
|
||||
0x9b, 0xe2, 0x49, 0xaf, 0xfd, 0x4c, 0x69, 0x46, 0x72, 0xe0, 0x2f, 0x31, 0x77, 0x86, 0x7b, 0x5b, 0x6d,
|
||||
0x49, 0xe6, 0xc7, 0x84, 0xd1, 0xdd, 0x56, 0x89, 0x8d, 0xbd, 0x07, 0x18, 0x01, 0x43, 0x70, 0x9b, 0x00,
|
||||
0x71, 0x16, 0x89, 0x66, 0x2e, 0xb6, 0x5f, 0x62, 0xeb, 0x96, 0xed, 0xf2, 0xdb, 0xdb, 0xcf, 0xdd, 0xa8,
|
||||
0xab, 0xde, 0x93, 0xb3, 0xdb, 0x54, 0xf0, 0x34, 0x4a, 0x28, 0xc3, 0x11, 0xf6, 0xb9, 0xd6, 0x45, 0x3f,
|
||||
0x07, 0xc0, 0x8e, 0x10, 0x7a, 0x2b, 0x56, 0x15, 0xbb, 0x00, 0x9d, 0x82, 0x27, 0xf2, 0x11, 0xa3, 0xda,
|
||||
0x03, 0xaa, 0x51, 0xc0, 0xfd, 0x90, 0xc8, 0x73, 0x81, 0xce, 0x97, 0x30, 0xa2, 0x54, 0x63, 0x6f, 0xfc,
|
||||
0x7f, 0x5b, 0x71, 0xec, 0x11, 0xb0, 0xa0, 0xc8, 0x74, 0x3a, 0xcc, 0x1b, 0x5e, 0xcd, 0x91, 0xa8, 0x18,
|
||||
0x92, 0xeb, 0x33, 0xc4, 0x6d, 0xb8, 0x16, 0x67, 0xe1, 0xc5, 0xa6, 0x26, 0x35, 0x48, 0xc4, 0xe7, 0x94,
|
||||
0xeb, 0xbb, 0xb8, 0xde, 0xd3, 0xe1, 0xc0, 0xcb, 0x00, 0x20, 0xf6, 0xbc, 0xa9, 0xc5, 0x70, 0xc4, 0xda,
|
||||
0x1b, 0x61, 0x0b, 0x9f, 0x0b, 0x19, 0x93, 0xaf, 0x8f, 0x40, 0xbb, 0x26, 0x79, 0x02, 0x03, 0x01, 0x00,
|
||||
0x01, 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01, 0x19, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
|
||||
0x16, 0x04, 0x14, 0xa3, 0xda, 0xe5, 0xef, 0xc3, 0x1c, 0x7a, 0xcf, 0x34, 0x2b, 0xa2, 0x42, 0x2b, 0x77,
|
||||
0xcb, 0x62, 0xfb, 0x4c, 0x28, 0x51, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
|
||||
0x80, 0x14, 0x9c, 0xe1, 0xad, 0x8f, 0xd4, 0x86, 0xd2, 0x1c, 0x7e, 0x48, 0x32, 0xf2, 0x28, 0xfe, 0x87,
|
||||
0x90, 0xe3, 0xb1, 0xc5, 0x8e, 0x30, 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x43, 0x30, 0x41, 0x30,
|
||||
0x3f, 0xa0, 0x3d, 0xa0, 0x3b, 0x86, 0x39, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x2f, 0x2f, 0x2f, 0x2f, 0x52,
|
||||
0x44, 0x32, 0x38, 0x31, 0x38, 0x37, 0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x2f, 0x43, 0x65, 0x72,
|
||||
0x74, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x50, 0x72, 0x6f, 0x64, 0x32, 0x4c, 0x53, 0x52, 0x41,
|
||||
0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x64, 0x06, 0x08,
|
||||
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x58, 0x30, 0x56, 0x30, 0x54, 0x06, 0x08, 0x2b,
|
||||
0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x48, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x2f, 0x2f, 0x2f,
|
||||
0x2f, 0x52, 0x44, 0x32, 0x38, 0x31, 0x38, 0x37, 0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x2f, 0x43,
|
||||
0x65, 0x72, 0x74, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x52, 0x44, 0x32, 0x38, 0x31, 0x38, 0x37,
|
||||
0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x5f, 0x50, 0x72, 0x6f, 0x64, 0x32, 0x4c, 0x53, 0x52, 0x41,
|
||||
0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0c, 0x06, 0x03,
|
||||
0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x17, 0x06, 0x08, 0x2b, 0x06, 0x01,
|
||||
0x04, 0x01, 0x82, 0x37, 0x12, 0x04, 0x0b, 0x16, 0x09, 0x54, 0x4c, 0x53, 0x7e, 0x42, 0x41, 0x53, 0x49,
|
||||
0x43, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
|
||||
0x82, 0x01, 0x01, 0x00, 0x55, 0xd5, 0x94, 0x3b, 0x06, 0xef, 0xf2, 0xb0, 0xf9, 0xd7, 0x36, 0x2a, 0x36,
|
||||
0xe0, 0xf1, 0xd9, 0x18, 0xc1, 0x89, 0x7e, 0xa2, 0xcf, 0x01, 0x6f, 0x22, 0x7b, 0x34, 0x81, 0xf0, 0x7a,
|
||||
0x45, 0x11, 0x6e, 0x75, 0x4b, 0x0b, 0xa8, 0xcd, 0x92, 0x57, 0x19, 0x80, 0xb7, 0x6e, 0x1a, 0x4d, 0x12,
|
||||
0x65, 0x91, 0x56, 0x38, 0x17, 0x22, 0xa2, 0x75, 0xae, 0xf9, 0x12, 0x75, 0x38, 0xf3, 0x19, 0x74, 0xea,
|
||||
0x87, 0x46, 0x1f, 0x98, 0x2c, 0x2f, 0xf9, 0xfc, 0xb4, 0xdc, 0x25, 0xa0, 0xd3, 0x34, 0x1b, 0xbc, 0x21,
|
||||
0xbb, 0x3d, 0x82, 0xad, 0x15, 0xc6, 0x3d, 0x02, 0x75, 0x33, 0x70, 0x25, 0x0a, 0x1a, 0xf7, 0x4c, 0xcb,
|
||||
0x84, 0xa3, 0xc1, 0x78, 0xe6, 0xf5, 0xa1, 0x44, 0x54, 0xc8, 0x34, 0xfd, 0xef, 0xbf, 0x86, 0x81, 0x9d,
|
||||
0x9a, 0x7e, 0xb6, 0xad, 0x71, 0x7e, 0xe4, 0xd9, 0x71, 0x6c, 0xb9, 0xe7, 0xf2, 0xd6, 0xd7, 0xbb, 0x66,
|
||||
0x5a, 0x30, 0xf5, 0x29, 0xae, 0x02, 0x39, 0x3d, 0xea, 0x7a, 0x79, 0x1b, 0x53, 0xc5, 0xbe, 0x8d, 0xfb,
|
||||
0xe2, 0xe4, 0x8e, 0xc2, 0x04, 0xb3, 0x0a, 0x94, 0x75, 0xa3, 0xbf, 0xd4, 0x87, 0xd2, 0x74, 0x15, 0x05,
|
||||
0x5e, 0xd5, 0x8f, 0x94, 0x23, 0x41, 0x13, 0x3f, 0xbd, 0xed, 0x21, 0x55, 0x96, 0xe9, 0xc4, 0x93, 0x34,
|
||||
0x7f, 0xaa, 0xea, 0xe7, 0xb1, 0x9a, 0xca, 0x25, 0x91, 0x18, 0xdf, 0x28, 0x05, 0x8e, 0x53, 0xb3, 0x8c,
|
||||
0x8d, 0xcc, 0xf3, 0xf4, 0x78, 0x76, 0x76, 0x7b, 0x82, 0xd6, 0x75, 0x7a, 0x7d, 0xb3, 0x23, 0x2c, 0xc7,
|
||||
0xbe, 0xa6, 0xb0, 0x50, 0x4d, 0x6c, 0xe2, 0x90, 0x85, 0x97, 0x77, 0x0d, 0x2f, 0xf5, 0x7b, 0xb0, 0xc6,
|
||||
0xad, 0xfa, 0x9a, 0x2c, 0xdf, 0xeb, 0x0d, 0x60, 0xd3, 0x0e, 0xa8, 0x5c, 0x43, 0xab, 0x09, 0x85, 0xa3,
|
||||
0xa9, 0x31, 0x66, 0xbd, 0xe4],
|
||||
vec![0x30, 0x82, 0x04, 0x59, 0x30, 0x82, 0x03, 0x45, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x05, 0x01,
|
||||
0x00, 0x00, 0x00, 0x02, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x30, 0x11,
|
||||
0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x06, 0x42, 0x65, 0x63, 0x6b, 0x65, 0x72,
|
||||
0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x32, 0x36, 0x32, 0x33, 0x32, 0x36, 0x34, 0x35, 0x5a,
|
||||
0x17, 0x0d, 0x33, 0x38, 0x30, 0x31, 0x31, 0x39, 0x30, 0x33, 0x31, 0x34, 0x30, 0x37, 0x5a, 0x30, 0x81,
|
||||
0xa6, 0x31, 0x81, 0xa3, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x20, 0x00, 0x6e, 0x00, 0x63,
|
||||
0x00, 0x61, 0x00, 0x63, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00,
|
||||
0x63, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x32, 0x00, 0x37, 0x30, 0x33, 0x06, 0x03, 0x55, 0x04,
|
||||
0x07, 0x1e, 0x2c, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x69,
|
||||
0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00, 0x63, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x32, 0x00,
|
||||
0x37, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x30, 0x43, 0x06, 0x03,
|
||||
0x55, 0x04, 0x05, 0x1e, 0x3c, 0x00, 0x31, 0x00, 0x42, 0x00, 0x63, 0x00, 0x4b, 0x00, 0x65, 0x00, 0x56,
|
||||
0x00, 0x33, 0x00, 0x4d, 0x00, 0x67, 0x00, 0x74, 0x00, 0x6a, 0x00, 0x55, 0x00, 0x74, 0x00, 0x6f, 0x00,
|
||||
0x32, 0x00, 0x50, 0x00, 0x49, 0x00, 0x68, 0x00, 0x35, 0x00, 0x52, 0x00, 0x57, 0x00, 0x56, 0x00, 0x36,
|
||||
0x00, 0x42, 0x00, 0x58, 0x00, 0x48, 0x00, 0x77, 0x00, 0x3d, 0x00, 0x0d, 0x00, 0x0a, 0x30, 0x58, 0x30,
|
||||
0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x0f, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41,
|
||||
0x00, 0xab, 0xac, 0x87, 0x11, 0x83, 0xbf, 0xe9, 0x48, 0x25, 0x00, 0x2c, 0x33, 0x31, 0x5e, 0x3d, 0x78,
|
||||
0xc8, 0x5f, 0x82, 0xcb, 0x36, 0x41, 0xf5, 0xb4, 0x65, 0x15, 0xee, 0x04, 0x31, 0xae, 0xe2, 0x48, 0x58,
|
||||
0x99, 0x7f, 0x4f, 0x90, 0x1d, 0xf7, 0x7c, 0xd7, 0xf8, 0x47, 0x93, 0xa0, 0xca, 0x9c, 0xdf, 0x91, 0xb0,
|
||||
0x41, 0xe8, 0x05, 0x4b, 0xdc, 0x24, 0x5b, 0x72, 0xf7, 0x68, 0x91, 0x84, 0xfb, 0x19, 0x02, 0x03, 0x01,
|
||||
0x00, 0x01, 0xa3, 0x82, 0x01, 0xf4, 0x30, 0x82, 0x01, 0xf0, 0x30, 0x14, 0x06, 0x09, 0x2b, 0x06, 0x01,
|
||||
0x04, 0x01, 0x82, 0x37, 0x12, 0x04, 0x01, 0x01, 0xff, 0x04, 0x04, 0x01, 0x00, 0x05, 0x00, 0x30, 0x3c,
|
||||
0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x02, 0x01, 0x01, 0xff, 0x04, 0x2c, 0x4d,
|
||||
0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00,
|
||||
0x20, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74,
|
||||
0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x30, 0x81, 0xdd, 0x06, 0x09, 0x2b, 0x06, 0x01,
|
||||
0x04, 0x01, 0x82, 0x37, 0x12, 0x05, 0x01, 0x01, 0xff, 0x04, 0x81, 0xcc, 0x00, 0x30, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x22, 0x04, 0x00, 0x00, 0x1c, 0x00, 0x4a, 0x00, 0x66, 0x00,
|
||||
0x4a, 0x00, 0xb0, 0x00, 0x03, 0x00, 0x33, 0x00, 0x64, 0x00, 0x32, 0x00, 0x36, 0x00, 0x37, 0x00, 0x39,
|
||||
0x00, 0x35, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x65, 0x00, 0x65, 0x00, 0x62, 0x00, 0x37, 0x00, 0x2d, 0x00,
|
||||
0x31, 0x00, 0x31, 0x00, 0x64, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x62, 0x00, 0x39, 0x00, 0x34, 0x00, 0x65,
|
||||
0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x63, 0x00, 0x30, 0x00, 0x34, 0x00, 0x66, 0x00, 0x61, 0x00,
|
||||
0x33, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00, 0x64, 0x00, 0x00, 0x00, 0x33, 0x00, 0x64, 0x00, 0x32,
|
||||
0x00, 0x36, 0x00, 0x37, 0x00, 0x39, 0x00, 0x35, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x65, 0x00, 0x65, 0x00,
|
||||
0x62, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x31, 0x00, 0x64, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x62,
|
||||
0x00, 0x39, 0x00, 0x34, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x63, 0x00, 0x30, 0x00,
|
||||
0x34, 0x00, 0x66, 0x00, 0x61, 0x00, 0x33, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00, 0x64, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x80, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x06, 0x09,
|
||||
0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x06, 0x01, 0x01, 0xff, 0x04, 0x70, 0x00, 0x30, 0x00,
|
||||
0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x34, 0x00,
|
||||
0x4c, 0x00, 0x34, 0x00, 0x4c, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x43, 0x00, 0x53,
|
||||
0x00, 0x51, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x34, 0x00, 0x32, 0x00, 0x39, 0x00, 0x2d, 0x00,
|
||||
0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x33, 0x00, 0x34, 0x00, 0x39,
|
||||
0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x41, 0x00, 0x54, 0x00, 0x33, 0x00, 0x35, 0x00, 0x33, 0x00,
|
||||
0x00, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x55,
|
||||
0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x37, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x01, 0x01, 0xff,
|
||||
0x04, 0x2d, 0x30, 0x2b, 0xa1, 0x22, 0xa4, 0x20, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x34,
|
||||
0x00, 0x4c, 0x00, 0x34, 0x00, 0x4c, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x43, 0x00,
|
||||
0x53, 0x00, 0x51, 0x00, 0x00, 0x00, 0x82, 0x05, 0x01, 0x00, 0x00, 0x00, 0x02, 0x30, 0x09, 0x06, 0x05,
|
||||
0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3e, 0xd3, 0xd5, 0x61, 0x8a,
|
||||
0x87, 0x7b, 0x98, 0x2c, 0x6d, 0x20, 0x38, 0x12, 0x08, 0xd8, 0xf7, 0x83, 0x08, 0xf8, 0xe6, 0xb2, 0xe1,
|
||||
0x21, 0xe1, 0x30, 0x61, 0x12, 0x19, 0xe8, 0xc1, 0x41, 0xaf, 0x59, 0x7c, 0x1e, 0x3e, 0xc8, 0x40, 0x9e,
|
||||
0x24, 0xe8, 0x8d, 0x0c, 0x41, 0xfd, 0xf8, 0x3e, 0xa1, 0xb3, 0xac, 0x56, 0xac, 0x52, 0x91, 0x5a, 0xf8,
|
||||
0xd0, 0x40, 0x8e, 0x13, 0x47, 0xa9, 0x8a, 0x0a, 0x62, 0x6d, 0x11, 0x89, 0x20, 0x56, 0xe7, 0xd6, 0x5f,
|
||||
0x12, 0x44, 0x94, 0xbf, 0x63, 0x99, 0xa3, 0x42, 0x40, 0xd5, 0xc6, 0x8c, 0x1f, 0x4b, 0xf8, 0xaf, 0x83,
|
||||
0x8e, 0xf6, 0x74, 0xb2, 0x0b, 0x55, 0x13, 0x4a, 0x76, 0xed, 0x37, 0xd8, 0x3d, 0x13, 0xe7, 0xae, 0x43,
|
||||
0x4c, 0x9a, 0x61, 0x6c, 0x7b, 0x1b, 0xd1, 0xaa, 0x00, 0x97, 0xdf, 0x5b, 0x85, 0x9f, 0xc8, 0xee, 0x6c,
|
||||
0xe5, 0xa2, 0x63, 0x76, 0xe4, 0x06, 0xd3, 0x2a, 0xe0, 0x55, 0xe1, 0x92, 0x78, 0xed, 0x03, 0x7b, 0x7d,
|
||||
0x1a, 0x6e, 0xc2, 0x56, 0xdc, 0xad, 0x6e, 0xd7, 0xa9, 0xfe, 0xa7, 0xfd, 0x09, 0x0a, 0xa6, 0xd5, 0x8a,
|
||||
0x99, 0xa4, 0x75, 0x89, 0xad, 0x84, 0xc7, 0x09, 0xf7, 0x4c, 0x6e, 0xd0, 0xe2, 0x80, 0x17, 0x62, 0xfa,
|
||||
0x86, 0xfe, 0x43, 0x51, 0xf2, 0xb4, 0xf6, 0xef, 0x3b, 0xb3, 0x3d, 0x1f, 0xef, 0xa3, 0xcb, 0xa2, 0x57,
|
||||
0x25, 0x7c, 0x02, 0xf2, 0x27, 0x1c, 0x87, 0x70, 0x8e, 0x84, 0x20, 0xfe, 0x1d, 0x4a, 0xc4, 0x87, 0x24,
|
||||
0x3b, 0xba, 0xff, 0x34, 0x1a, 0xe2, 0xff, 0xa2, 0x43, 0x39, 0xd8, 0x19, 0x97, 0xf8, 0xf0, 0xf9, 0x73,
|
||||
0xa6, 0xb6, 0x55, 0x64, 0xa6, 0xca, 0xa3, 0x48, 0x22, 0xb7, 0x1a, 0x9b, 0x98, 0x1a, 0x8e, 0x2f, 0xaa,
|
||||
0xec, 0xc1, 0xfe, 0x25, 0x36, 0x2b, 0x70, 0x97, 0x8c, 0x5b, 0x62, 0x21, 0xc3],
|
||||
],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
scope_list: vec![Scope(String::from("microsoft.com"))],
|
||||
scope_list: vec![Scope(String::from("microsoft.com"))],
|
||||
};
|
||||
req.license_header.preamble_message_size = req.size() as u16;
|
||||
req.into()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -281,17 +294,25 @@ fn buffer_length_is_correct_for_client_new_license_request() {
|
|||
|
||||
#[test]
|
||||
fn client_new_license_request_creates_correctly() {
|
||||
let (client_new_license_request, encryption_data) = ClientNewLicenseRequest::from_server_license_request(
|
||||
&SERVER_LICENSE_REQUEST,
|
||||
CLIENT_RANDOM_BUFFER.as_ref(),
|
||||
PREMASTER_SECRET_BUFFER.as_ref(),
|
||||
CLIENT_USERNAME,
|
||||
CLIENT_MACHINE_NAME,
|
||||
)
|
||||
.unwrap();
|
||||
match &*SERVER_LICENSE_REQUEST {
|
||||
LicensePdu::ServerLicenseRequest(license_request) => {
|
||||
let (client_new_license_request, encryption_data) = ClientNewLicenseRequest::from_server_license_request(
|
||||
license_request,
|
||||
CLIENT_RANDOM_BUFFER.as_ref(),
|
||||
PREMASTER_SECRET_BUFFER.as_ref(),
|
||||
CLIENT_USERNAME,
|
||||
CLIENT_MACHINE_NAME,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(encryption_data.license_key, LICENSE_KEY_BUFFER.as_ref());
|
||||
assert_eq!(client_new_license_request, *CLIENT_NEW_LICENSE_REQUEST);
|
||||
assert_eq!(encryption_data.license_key, LICENSE_KEY_BUFFER.as_ref());
|
||||
assert_eq!(
|
||||
Into::<LicensePdu>::into(client_new_license_request),
|
||||
*CLIENT_NEW_LICENSE_REQUEST
|
||||
);
|
||||
}
|
||||
_ => panic!("Invalid license pdu"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -100,16 +100,16 @@ impl ClientPlatformChallengeResponse {
|
|||
}
|
||||
}
|
||||
|
||||
impl PduEncode for ClientPlatformChallengeResponse {
|
||||
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
impl ClientPlatformChallengeResponse {
|
||||
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
ensure_size!(in: dst, size: self.size());
|
||||
|
||||
self.license_header.encode(dst)?;
|
||||
|
||||
BlobHeader::new(BlobType::EncryptedData, self.encrypted_challenge_response_data.len()).encode(dst)?;
|
||||
BlobHeader::new(BlobType::ENCRYPTED_DATA, self.encrypted_challenge_response_data.len()).encode(dst)?;
|
||||
dst.write_slice(&self.encrypted_challenge_response_data);
|
||||
|
||||
BlobHeader::new(BlobType::EncryptedData, self.encrypted_hwid.len()).encode(dst)?;
|
||||
BlobHeader::new(BlobType::ENCRYPTED_DATA, self.encrypted_hwid.len()).encode(dst)?;
|
||||
dst.write_slice(&self.encrypted_hwid);
|
||||
|
||||
dst.write_slice(&self.mac_data);
|
||||
|
|
@ -117,20 +117,19 @@ impl PduEncode for ClientPlatformChallengeResponse {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
pub fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
pub fn size(&self) -> usize {
|
||||
self.license_header.size()
|
||||
+ (BLOB_TYPE_SIZE + BLOB_LENGTH_SIZE) * 2 // 2 blobs in this structure
|
||||
+ MAC_SIZE + self.encrypted_challenge_response_data.len() + self.encrypted_hwid.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> PduDecode<'de> for ClientPlatformChallengeResponse {
|
||||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
let license_header = LicenseHeader::decode(src)?;
|
||||
impl ClientPlatformChallengeResponse {
|
||||
pub fn decode(license_header: LicenseHeader, src: &mut ReadCursor<'_>) -> PduResult<Self> {
|
||||
if license_header.preamble_message_type != PreambleType::PlatformChallengeResponse {
|
||||
return Err(invalid_message_err!(
|
||||
"preambleMessageType",
|
||||
|
|
@ -139,14 +138,14 @@ impl<'de> PduDecode<'de> for ClientPlatformChallengeResponse {
|
|||
}
|
||||
|
||||
let encrypted_challenge_blob = BlobHeader::decode(src)?;
|
||||
if encrypted_challenge_blob.blob_type != BlobType::EncryptedData {
|
||||
if encrypted_challenge_blob.blob_type != BlobType::ENCRYPTED_DATA {
|
||||
return Err(invalid_message_err!("blobType", "unexpected blob type"));
|
||||
}
|
||||
ensure_size!(in: src, size: encrypted_challenge_blob.length);
|
||||
let encrypted_challenge_response_data = src.read_slice(encrypted_challenge_blob.length).into();
|
||||
|
||||
let encrypted_hwid_blob = BlobHeader::decode(src)?;
|
||||
if encrypted_hwid_blob.blob_type != BlobType::EncryptedData {
|
||||
if encrypted_hwid_blob.blob_type != BlobType::ENCRYPTED_DATA {
|
||||
return Err(invalid_message_err!("blobType", "unexpected blob type"));
|
||||
}
|
||||
ensure_size!(in: src, size: encrypted_hwid_blob.length);
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ use lazy_static::lazy_static;
|
|||
|
||||
use super::*;
|
||||
use crate::rdp::server_license::{
|
||||
BasicSecurityHeader, BasicSecurityHeaderFlags, LicenseHeader, PreambleFlags, PreambleType, PreambleVersion,
|
||||
BASIC_SECURITY_HEADER_SIZE, PREAMBLE_SIZE,
|
||||
BasicSecurityHeader, BasicSecurityHeaderFlags, LicenseHeader, LicensePdu, PreambleFlags, PreambleType,
|
||||
PreambleVersion, BASIC_SECURITY_HEADER_SIZE, PREAMBLE_SIZE,
|
||||
};
|
||||
use crate::{decode, encode_vec};
|
||||
|
||||
|
|
@ -57,8 +57,8 @@ lazy_static! {
|
|||
platform_id: HARDWARE_ID,
|
||||
data: Vec::from(DATA_BUFFER.as_ref()),
|
||||
};
|
||||
pub(crate) static ref CLIENT_PLATFORM_CHALLENGE_RESPONSE: ClientPlatformChallengeResponse =
|
||||
ClientPlatformChallengeResponse {
|
||||
pub(crate) static ref CLIENT_PLATFORM_CHALLENGE_RESPONSE: LicensePdu =
|
||||
LicensePdu::ClientPlatformChallengeResponse(ClientPlatformChallengeResponse {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
|
|
@ -74,7 +74,7 @@ lazy_static! {
|
|||
mac_data: Vec::from(
|
||||
&CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER[CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER.len() - 16..]
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -4,9 +4,13 @@ mod test;
|
|||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
|
||||
use super::{BlobHeader, BlobType, BLOB_LENGTH_SIZE, BLOB_TYPE_SIZE};
|
||||
use super::{BlobHeader, BlobType, LicenseHeader, PreambleFlags, PreambleVersion, BLOB_LENGTH_SIZE, BLOB_TYPE_SIZE};
|
||||
use crate::{
|
||||
cursor::{ReadCursor, WriteCursor},
|
||||
rdp::{
|
||||
headers::{BasicSecurityHeader, BasicSecurityHeaderFlags},
|
||||
server_license::PreambleType,
|
||||
},
|
||||
PduDecode, PduEncode, PduResult,
|
||||
};
|
||||
|
||||
|
|
@ -18,6 +22,7 @@ const STATE_TRANSITION_SIZE: usize = 4;
|
|||
/// [2.2.1.12.1.3]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/f18b6c9f-f3d8-4a0e-8398-f9b153233dca
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct LicensingErrorMessage {
|
||||
pub license_header: LicenseHeader,
|
||||
pub error_code: LicenseErrorCode,
|
||||
pub state_transition: LicensingStateTransition,
|
||||
pub error_info: Vec<u8>,
|
||||
|
|
@ -27,46 +32,73 @@ impl LicensingErrorMessage {
|
|||
const NAME: &'static str = "LicensingErrorMessage";
|
||||
|
||||
const FIXED_PART_SIZE: usize = ERROR_CODE_SIZE + STATE_TRANSITION_SIZE;
|
||||
|
||||
pub fn new_valid_client() -> PduResult<Self> {
|
||||
let mut this = Self {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
},
|
||||
preamble_message_type: PreambleType::ErrorAlert,
|
||||
preamble_flags: PreambleFlags::empty(),
|
||||
preamble_version: PreambleVersion::V3,
|
||||
preamble_message_size: 0,
|
||||
},
|
||||
error_code: LicenseErrorCode::StatusValidClient,
|
||||
state_transition: LicensingStateTransition::NoTransition,
|
||||
error_info: Vec::new(),
|
||||
};
|
||||
this.license_header.preamble_message_size =
|
||||
cast_length!("LicensingErrorMessage", "preamble_message_size", this.size())?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl PduEncode for LicensingErrorMessage {
|
||||
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
impl LicensingErrorMessage {
|
||||
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
ensure_size!(in: dst, size: self.size());
|
||||
|
||||
self.license_header.encode(dst)?;
|
||||
|
||||
dst.write_u32(self.error_code.to_u32().unwrap());
|
||||
dst.write_u32(self.state_transition.to_u32().unwrap());
|
||||
|
||||
BlobHeader::new(BlobType::Error, self.error_info.len()).encode(dst)?;
|
||||
BlobHeader::new(BlobType::ERROR, self.error_info.len()).encode(dst)?;
|
||||
dst.write_slice(&self.error_info);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
pub fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
Self::FIXED_PART_SIZE + self.error_info.len() + BLOB_LENGTH_SIZE + BLOB_TYPE_SIZE
|
||||
pub fn size(&self) -> usize {
|
||||
self.license_header.size() + Self::FIXED_PART_SIZE + self.error_info.len() + BLOB_LENGTH_SIZE + BLOB_TYPE_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> PduDecode<'de> for LicensingErrorMessage {
|
||||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
ensure_fixed_part_size!(in: src);
|
||||
impl LicensingErrorMessage {
|
||||
pub fn decode(license_header: LicenseHeader, src: &mut ReadCursor<'_>) -> PduResult<Self> {
|
||||
if license_header.preamble_message_type != PreambleType::ErrorAlert {
|
||||
return Err(invalid_message_err!("preambleMessageType", "unexpected preamble type"));
|
||||
}
|
||||
|
||||
ensure_fixed_part_size!(in: src);
|
||||
let error_code = LicenseErrorCode::from_u32(src.read_u32())
|
||||
.ok_or_else(|| invalid_message_err!("errorCode", "invalid error code"))?;
|
||||
let state_transition = LicensingStateTransition::from_u32(src.read_u32())
|
||||
.ok_or_else(|| invalid_message_err!("stateTransition", "invalid state transition"))?;
|
||||
|
||||
let error_info_blob = BlobHeader::decode(src)?;
|
||||
if error_info_blob.blob_type != BlobType::Error {
|
||||
if error_info_blob.length != 0 && error_info_blob.blob_type != BlobType::ERROR {
|
||||
return Err(invalid_message_err!("blobType", "invalid blob type"));
|
||||
}
|
||||
|
||||
let error_info = vec![0u8; error_info_blob.length];
|
||||
|
||||
Ok(Self {
|
||||
license_header,
|
||||
error_code,
|
||||
state_transition,
|
||||
error_info,
|
||||
|
|
|
|||
|
|
@ -1,33 +1,63 @@
|
|||
use lazy_static::lazy_static;
|
||||
|
||||
use super::*;
|
||||
use crate::{decode, encode_vec};
|
||||
use crate::{
|
||||
decode, encode_vec,
|
||||
rdp::{
|
||||
headers::{BasicSecurityHeader, BasicSecurityHeaderFlags},
|
||||
server_license::{LicensePdu, PreambleFlags, PreambleVersion},
|
||||
},
|
||||
};
|
||||
|
||||
pub const LICENSE_MESSAGE_BUFFER: [u8; 12] = [
|
||||
const HEADER_MESSAGE_BUFFER: [u8; 8] = [0x80, 0x00, 0x00, 0x00, 0xFF, 0x03, 0x14, 0x00];
|
||||
|
||||
const LICENSE_MESSAGE_BUFFER: [u8; 12] = [
|
||||
0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // message
|
||||
];
|
||||
|
||||
lazy_static! {
|
||||
pub static ref LICENSING_ERROR_MESSAGE: LicensingErrorMessage = LicensingErrorMessage {
|
||||
error_code: LicenseErrorCode::StatusValidClient,
|
||||
state_transition: LicensingStateTransition::NoTransition,
|
||||
error_info: Vec::new(),
|
||||
pub static ref LICENSING_ERROR_MESSAGE: LicensePdu = {
|
||||
let mut pdu = LicensingErrorMessage {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
},
|
||||
preamble_message_type: PreambleType::ErrorAlert,
|
||||
preamble_flags: PreambleFlags::empty(),
|
||||
preamble_version: PreambleVersion::V3,
|
||||
preamble_message_size: 0,
|
||||
},
|
||||
error_code: LicenseErrorCode::StatusValidClient,
|
||||
state_transition: LicensingStateTransition::NoTransition,
|
||||
error_info: Vec::new(),
|
||||
};
|
||||
pdu.license_header.preamble_message_size = pdu.size() as u16;
|
||||
pdu.into()
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_buffer_correctly_parses_licensing_error_message() {
|
||||
assert_eq!(*LICENSING_ERROR_MESSAGE, decode(&LICENSE_MESSAGE_BUFFER).unwrap(),);
|
||||
assert_eq!(
|
||||
*LICENSING_ERROR_MESSAGE,
|
||||
decode(&[&HEADER_MESSAGE_BUFFER[..], &LICENSE_MESSAGE_BUFFER[..]].concat()).unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_buffer_correctly_serializes_licensing_error_message() {
|
||||
let buffer = encode_vec(&*LICENSING_ERROR_MESSAGE).unwrap();
|
||||
|
||||
assert_eq!(LICENSE_MESSAGE_BUFFER.as_ref(), buffer.as_slice());
|
||||
assert_eq!(
|
||||
[&HEADER_MESSAGE_BUFFER[..], &LICENSE_MESSAGE_BUFFER[..]].concat(),
|
||||
buffer
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buffer_length_is_correct_for_licensing_error_message() {
|
||||
assert_eq!(LICENSE_MESSAGE_BUFFER.len(), LICENSING_ERROR_MESSAGE.size());
|
||||
assert_eq!(
|
||||
HEADER_MESSAGE_BUFFER.len() + LICENSE_MESSAGE_BUFFER.len(),
|
||||
LICENSING_ERROR_MESSAGE.size()
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ mod tests;
|
|||
use cert::{CertificateType, ProprietaryCertificate, X509CertificateChain};
|
||||
|
||||
use super::{
|
||||
BasicSecurityHeader, BasicSecurityHeaderFlags, BlobHeader, BlobType, LicenseErrorCode, LicenseHeader,
|
||||
LicensingErrorMessage, LicensingStateTransition, PreambleFlags, PreambleType, PreambleVersion, ServerLicenseError,
|
||||
BLOB_LENGTH_SIZE, BLOB_TYPE_SIZE, KEY_EXCHANGE_ALGORITHM_RSA, PREAMBLE_SIZE, RANDOM_NUMBER_SIZE,
|
||||
UTF16_NULL_TERMINATOR_SIZE, UTF8_NULL_TERMINATOR_SIZE,
|
||||
BlobHeader, BlobType, LicenseHeader, PreambleType, ServerLicenseError, BLOB_LENGTH_SIZE, BLOB_TYPE_SIZE,
|
||||
KEY_EXCHANGE_ALGORITHM_RSA, RANDOM_NUMBER_SIZE, UTF16_NULL_TERMINATOR_SIZE, UTF8_NULL_TERMINATOR_SIZE,
|
||||
};
|
||||
use crate::{
|
||||
cursor::{ReadCursor, WriteCursor},
|
||||
|
|
@ -28,118 +26,12 @@ const MAX_PRODUCT_ID_LEN: usize = 1024;
|
|||
|
||||
const RSA_EXCHANGE_ALGORITHM: u32 = 1;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum InitialMessageType {
|
||||
LicenseRequest(ServerLicenseRequest),
|
||||
StatusValidClient(LicensingErrorMessage),
|
||||
}
|
||||
|
||||
// FIXME(#269): this is a helper structure which tries to detect if a
|
||||
// SERVER_LICENSE_REQUEST PDU is received from the server, or if a
|
||||
// STATUS_VALID_CLIENT error code is received instead (no need to negotiate
|
||||
// a license). I think this could be refactored into a more generic struct / enum,
|
||||
// without trying to be too smart by, e.g., returning errors when a LICENSE_ERROR_MESSAGE
|
||||
// is received depending on the error code. Parsing code should lend the data received
|
||||
// from the network without making too much decisions.
|
||||
|
||||
/// Either a SERVER_LICENSE_REQUEST or a LICENSE_ERROR_MESSAGE with the STATUS_VALID_CLIENT code
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct InitialServerLicenseMessage {
|
||||
pub license_header: LicenseHeader,
|
||||
pub message_type: InitialMessageType,
|
||||
}
|
||||
|
||||
impl InitialServerLicenseMessage {
|
||||
const NAME: &'static str = "InitialServerLicenseMessage";
|
||||
|
||||
pub fn new_status_valid_client_message() -> Self {
|
||||
let valid_client_message = LicensingErrorMessage {
|
||||
error_code: LicenseErrorCode::StatusValidClient,
|
||||
state_transition: LicensingStateTransition::NoTransition,
|
||||
error_info: Vec::new(),
|
||||
};
|
||||
|
||||
Self {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
},
|
||||
preamble_message_type: PreambleType::ErrorAlert,
|
||||
preamble_flags: PreambleFlags::empty(),
|
||||
preamble_version: PreambleVersion::V3,
|
||||
preamble_message_size: (PREAMBLE_SIZE + valid_client_message.size()) as u16,
|
||||
},
|
||||
message_type: InitialMessageType::StatusValidClient(valid_client_message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PduEncode for InitialServerLicenseMessage {
|
||||
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
self.license_header.encode(dst)?;
|
||||
|
||||
match &self.message_type {
|
||||
InitialMessageType::LicenseRequest(license_request) => {
|
||||
license_request.encode(dst)?;
|
||||
}
|
||||
InitialMessageType::StatusValidClient(valid_client) => {
|
||||
valid_client.encode(dst)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.license_header.size()
|
||||
+ match &self.message_type {
|
||||
InitialMessageType::LicenseRequest(license_request) => license_request.size(),
|
||||
InitialMessageType::StatusValidClient(valid_client) => valid_client.size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> PduDecode<'de> for InitialServerLicenseMessage {
|
||||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
let license_header = LicenseHeader::decode(src)?;
|
||||
|
||||
match license_header.preamble_message_type {
|
||||
PreambleType::LicenseRequest => {
|
||||
let license_request = ServerLicenseRequest::decode(src)?;
|
||||
|
||||
Ok(Self {
|
||||
license_header,
|
||||
message_type: InitialMessageType::LicenseRequest(license_request),
|
||||
})
|
||||
}
|
||||
PreambleType::ErrorAlert => {
|
||||
let license_error = LicensingErrorMessage::decode(src)?;
|
||||
|
||||
if license_error.error_code == LicenseErrorCode::StatusValidClient
|
||||
&& license_error.state_transition == LicensingStateTransition::NoTransition
|
||||
{
|
||||
Ok(Self {
|
||||
license_header,
|
||||
message_type: InitialMessageType::StatusValidClient(license_error),
|
||||
})
|
||||
} else {
|
||||
Err(invalid_message_err!("errorCode", "unexpected error"))
|
||||
}
|
||||
}
|
||||
_ => Err(invalid_message_err!("preambleMessageType", "invalid preamble")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [2.2.2.1] Server License Request (SERVER_LICENSE_REQUEST)
|
||||
///
|
||||
/// [2.2.2.1]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpele/e17772e9-9642-4bb6-a2bc-82875dd6da7c
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ServerLicenseRequest {
|
||||
pub license_header: LicenseHeader,
|
||||
pub server_random: Vec<u8>,
|
||||
pub product_info: ProductInfo,
|
||||
pub server_certificate: Option<ServerCertificate>,
|
||||
|
|
@ -154,18 +46,20 @@ impl ServerLicenseRequest {
|
|||
}
|
||||
}
|
||||
|
||||
impl PduEncode for ServerLicenseRequest {
|
||||
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
impl ServerLicenseRequest {
|
||||
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
ensure_size!(in: dst, size: self.size());
|
||||
|
||||
self.license_header.encode(dst)?;
|
||||
|
||||
dst.write_slice(&self.server_random);
|
||||
self.product_info.encode(dst)?;
|
||||
|
||||
BlobHeader::new(BlobType::KeyExchangeAlgorithm, KEY_EXCHANGE_FIELD_SIZE).encode(dst)?;
|
||||
BlobHeader::new(BlobType::KEY_EXCHANGE_ALGORITHM, KEY_EXCHANGE_FIELD_SIZE).encode(dst)?;
|
||||
dst.write_u32(KEY_EXCHANGE_ALGORITHM_RSA);
|
||||
|
||||
let cert_size = self.server_certificate.as_ref().map(|v| v.size()).unwrap_or(0);
|
||||
BlobHeader::new(BlobType::Certificate, cert_size).encode(dst)?;
|
||||
BlobHeader::new(BlobType::CERTIFICATE, cert_size).encode(dst)?;
|
||||
|
||||
if let Some(cert) = &self.server_certificate {
|
||||
cert.encode(dst)?;
|
||||
|
|
@ -180,12 +74,13 @@ impl PduEncode for ServerLicenseRequest {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
pub fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
RANDOM_NUMBER_SIZE
|
||||
pub fn size(&self) -> usize {
|
||||
self.license_header.size()
|
||||
+ RANDOM_NUMBER_SIZE
|
||||
+ self.product_info.size()
|
||||
+ BLOB_LENGTH_SIZE * 2 // KeyExchangeBlob + CertificateBlob
|
||||
+ BLOB_TYPE_SIZE * 2 // KeyExchangeBlob + CertificateBlob
|
||||
|
|
@ -196,15 +91,19 @@ impl PduEncode for ServerLicenseRequest {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'de> PduDecode<'de> for ServerLicenseRequest {
|
||||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
impl ServerLicenseRequest {
|
||||
pub fn decode(license_header: LicenseHeader, src: &mut ReadCursor<'_>) -> PduResult<Self> {
|
||||
if license_header.preamble_message_type != PreambleType::LicenseRequest {
|
||||
return Err(invalid_message_err!("preambleMessageType", "unexpected preamble type"));
|
||||
}
|
||||
|
||||
ensure_size!(in: src, size: RANDOM_NUMBER_SIZE);
|
||||
let server_random = src.read_slice(RANDOM_NUMBER_SIZE).into();
|
||||
|
||||
let product_info = ProductInfo::decode(src)?;
|
||||
|
||||
let key_exchange_algorithm_blob = BlobHeader::decode(src)?;
|
||||
if key_exchange_algorithm_blob.blob_type != BlobType::KeyExchangeAlgorithm {
|
||||
if key_exchange_algorithm_blob.blob_type != BlobType::KEY_EXCHANGE_ALGORITHM {
|
||||
return Err(invalid_message_err!("blobType", "invalid blob type"));
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +114,7 @@ impl<'de> PduDecode<'de> for ServerLicenseRequest {
|
|||
}
|
||||
|
||||
let cert_blob = BlobHeader::decode(src)?;
|
||||
if cert_blob.blob_type != BlobType::Certificate {
|
||||
if cert_blob.blob_type != BlobType::CERTIFICATE {
|
||||
return Err(invalid_message_err!("blobType", "invalid blob type"));
|
||||
}
|
||||
|
||||
|
|
@ -239,6 +138,7 @@ impl<'de> PduDecode<'de> for ServerLicenseRequest {
|
|||
}
|
||||
|
||||
Ok(Self {
|
||||
license_header,
|
||||
server_random,
|
||||
product_info,
|
||||
server_certificate,
|
||||
|
|
@ -261,7 +161,7 @@ impl PduEncode for Scope {
|
|||
ensure_size!(in: dst, size: self.size());
|
||||
|
||||
let data_size = self.0.len() + UTF8_NULL_TERMINATOR_SIZE;
|
||||
BlobHeader::new(BlobType::Scope, data_size).encode(dst)?;
|
||||
BlobHeader::new(BlobType::SCOPE, data_size).encode(dst)?;
|
||||
dst.write_slice(self.0.as_bytes());
|
||||
dst.write_u8(0); // null terminator
|
||||
|
||||
|
|
@ -280,7 +180,7 @@ impl PduEncode for Scope {
|
|||
impl<'de> PduDecode<'de> for Scope {
|
||||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
let blob_header = BlobHeader::decode(src)?;
|
||||
if blob_header.blob_type != BlobType::Scope {
|
||||
if blob_header.blob_type != BlobType::SCOPE {
|
||||
return Err(invalid_message_err!("blobType", "invalid blob type"));
|
||||
}
|
||||
if blob_header.length < UTF8_NULL_TERMINATOR_SIZE {
|
||||
|
|
|
|||
|
|
@ -120,10 +120,10 @@ impl PduEncode for ProprietaryCertificate {
|
|||
dst.write_u32(SIGNATURE_ALGORITHM_RSA);
|
||||
dst.write_u32(KEY_EXCHANGE_ALGORITHM_RSA);
|
||||
|
||||
BlobHeader::new(BlobType::RsaKey, self.public_key.size()).encode(dst)?;
|
||||
BlobHeader::new(BlobType::RSA_KEY, self.public_key.size()).encode(dst)?;
|
||||
self.public_key.encode(dst)?;
|
||||
|
||||
BlobHeader::new(BlobType::RsaSignature, self.signature.len()).encode(dst)?;
|
||||
BlobHeader::new(BlobType::RSA_SIGNATURE, self.signature.len()).encode(dst)?;
|
||||
dst.write_slice(&self.signature);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -153,13 +153,13 @@ impl<'de> PduDecode<'de> for ProprietaryCertificate {
|
|||
}
|
||||
|
||||
let key_blob_header = BlobHeader::decode(src)?;
|
||||
if key_blob_header.blob_type != BlobType::RsaKey {
|
||||
if key_blob_header.blob_type != BlobType::RSA_KEY {
|
||||
return Err(invalid_message_err!("blobType", "invalid blob type"));
|
||||
}
|
||||
let public_key = RsaPublicKey::decode(src)?;
|
||||
|
||||
let sig_blob_header = BlobHeader::decode(src)?;
|
||||
if sig_blob_header.blob_type != BlobType::RsaSignature {
|
||||
if sig_blob_header.blob_type != BlobType::RSA_SIGNATURE {
|
||||
return Err(invalid_message_err!("blobType", "invalid blob type"));
|
||||
}
|
||||
ensure_size!(in: src, size: sig_blob_header.length);
|
||||
|
|
|
|||
|
|
@ -2,8 +2,13 @@ use lazy_static::lazy_static;
|
|||
|
||||
use super::cert::{RsaPublicKey, PROP_CERT_BLOBS_HEADERS_SIZE, PROP_CERT_NO_BLOBS_SIZE, RSA_KEY_SIZE_WITHOUT_MODULUS};
|
||||
use super::*;
|
||||
use crate::rdp::headers::{BasicSecurityHeader, BasicSecurityHeaderFlags};
|
||||
use crate::rdp::server_license::{LicensePdu, PreambleFlags, PreambleVersion};
|
||||
use crate::{decode, encode_vec, PduEncode};
|
||||
|
||||
const LICENSE_HEADER_BUFFER_WITH_CERT: [u8; 8] = [0x80, 0x00, 0x00, 0x00, 0x01, 0x03, 0x9C, 0x08];
|
||||
const LICENSE_HEADER_BUFFER_NO_CERT: [u8; 8] = [0x80, 0x00, 0x00, 0x00, 0x01, 0x03, 0x8A, 0x00];
|
||||
|
||||
const SERVER_RANDOM_BUFFER: [u8; 32] = [
|
||||
0x84, 0xef, 0xae, 0x20, 0xb1, 0xd5, 0x9e, 0x36, 0x49, 0x1a, 0xe8, 0x2e, 0x0a, 0x99, 0x89, 0xac, 0x49, 0xa6, 0x47,
|
||||
0x4f, 0x33, 0x9b, 0x5a, 0xb9, 0x95, 0x03, 0xa6, 0xc6, 0xc2, 0x3c, 0x3f, 0x61,
|
||||
|
|
@ -222,20 +227,33 @@ lazy_static! {
|
|||
public_exponent: 0x0001_0001,
|
||||
modulus: Vec::from(MODULUS.as_ref()),
|
||||
};
|
||||
pub static ref SERVER_LICENSE_REQUEST: ServerLicenseRequest = ServerLicenseRequest {
|
||||
server_random: Vec::from(SERVER_RANDOM_BUFFER.as_ref()),
|
||||
product_info: ProductInfo {
|
||||
version: 0x60000,
|
||||
company_name: "Microsoft Corporation".to_string(),
|
||||
product_id: "A02".to_string(),
|
||||
},
|
||||
server_certificate: Some(ServerCertificate {
|
||||
issued_permanently: true,
|
||||
certificate: CertificateType::X509(X509CertificateChain {
|
||||
certificate_array: vec![Vec::from(CERT_1_BUFFER.as_ref()), Vec::from(CERT_2_BUFFER.as_ref()),],
|
||||
pub static ref SERVER_LICENSE_REQUEST: LicensePdu = {
|
||||
let mut req = ServerLicenseRequest {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
},
|
||||
preamble_message_type: PreambleType::LicenseRequest,
|
||||
preamble_flags: PreambleFlags::empty(),
|
||||
preamble_version: PreambleVersion::V3,
|
||||
preamble_message_size: 0,
|
||||
},
|
||||
server_random: Vec::from(SERVER_RANDOM_BUFFER.as_ref()),
|
||||
product_info: ProductInfo {
|
||||
version: 0x60000,
|
||||
company_name: "Microsoft Corporation".to_string(),
|
||||
product_id: "A02".to_string(),
|
||||
},
|
||||
server_certificate: Some(ServerCertificate {
|
||||
issued_permanently: true,
|
||||
certificate: CertificateType::X509(X509CertificateChain {
|
||||
certificate_array: vec![Vec::from(CERT_1_BUFFER.as_ref()), Vec::from(CERT_2_BUFFER.as_ref())],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
scope_list: vec![Scope(String::from("microsoft.com"))],
|
||||
scope_list: vec![Scope(String::from("microsoft.com"))],
|
||||
};
|
||||
req.license_header.preamble_message_size = req.size() as u16;
|
||||
req.into()
|
||||
};
|
||||
pub static ref X509_CERTIFICATE: ServerCertificate = ServerCertificate {
|
||||
issued_permanently: true,
|
||||
|
|
@ -252,6 +270,7 @@ lazy_static! {
|
|||
#[test]
|
||||
fn from_buffer_correctly_parses_server_license_request() {
|
||||
let request_buffer = [
|
||||
&LICENSE_HEADER_BUFFER_WITH_CERT[..],
|
||||
&SERVER_RANDOM_BUFFER[..],
|
||||
&PRODUCT_INFO_BUFFER[..],
|
||||
&KEY_EXCHANGE_LIST_BUFFER[..],
|
||||
|
|
@ -276,6 +295,7 @@ fn from_buffer_correctly_parses_server_license_request_no_certificate() {
|
|||
];
|
||||
|
||||
let request_buffer = [
|
||||
&LICENSE_HEADER_BUFFER_NO_CERT[..],
|
||||
&SERVER_RANDOM_BUFFER[..],
|
||||
&PRODUCT_INFO_BUFFER[..],
|
||||
&KEY_EXCHANGE_LIST_BUFFER[..],
|
||||
|
|
@ -284,7 +304,16 @@ fn from_buffer_correctly_parses_server_license_request_no_certificate() {
|
|||
]
|
||||
.concat();
|
||||
|
||||
let request = ServerLicenseRequest {
|
||||
let mut request = ServerLicenseRequest {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
},
|
||||
preamble_message_type: PreambleType::LicenseRequest,
|
||||
preamble_flags: PreambleFlags::empty(),
|
||||
preamble_version: PreambleVersion::V3,
|
||||
preamble_message_size: 0,
|
||||
},
|
||||
server_random: Vec::from(SERVER_RANDOM_BUFFER.as_ref()),
|
||||
product_info: ProductInfo {
|
||||
version: 0x60000,
|
||||
|
|
@ -294,6 +323,8 @@ fn from_buffer_correctly_parses_server_license_request_no_certificate() {
|
|||
server_certificate: None,
|
||||
scope_list: vec![Scope(String::from("microsoft.com"))],
|
||||
};
|
||||
request.license_header.preamble_message_size = request.size() as u16;
|
||||
let request: LicensePdu = request.into();
|
||||
|
||||
assert_eq!(request, decode(&request_buffer).unwrap());
|
||||
}
|
||||
|
|
@ -301,6 +332,7 @@ fn from_buffer_correctly_parses_server_license_request_no_certificate() {
|
|||
#[test]
|
||||
fn to_buffer_correctly_serializes_server_license_request() {
|
||||
let request_buffer = [
|
||||
&LICENSE_HEADER_BUFFER_WITH_CERT[..],
|
||||
&SERVER_RANDOM_BUFFER[..],
|
||||
&PRODUCT_INFO_BUFFER[..],
|
||||
&KEY_EXCHANGE_LIST_BUFFER[..],
|
||||
|
|
@ -314,7 +346,16 @@ fn to_buffer_correctly_serializes_server_license_request() {
|
|||
]
|
||||
.concat();
|
||||
|
||||
let request = ServerLicenseRequest {
|
||||
let mut request = ServerLicenseRequest {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
},
|
||||
preamble_message_type: PreambleType::LicenseRequest,
|
||||
preamble_flags: PreambleFlags::empty(),
|
||||
preamble_version: PreambleVersion::V3,
|
||||
preamble_message_size: 0,
|
||||
},
|
||||
server_random: Vec::from(SERVER_RANDOM_BUFFER.as_ref()),
|
||||
product_info: ProductInfo {
|
||||
version: 0x60000,
|
||||
|
|
@ -329,6 +370,8 @@ fn to_buffer_correctly_serializes_server_license_request() {
|
|||
}),
|
||||
scope_list: vec![Scope(String::from("microsoft.com"))],
|
||||
};
|
||||
request.license_header.preamble_message_size = request.size() as u16;
|
||||
let request: LicensePdu = request.into();
|
||||
|
||||
let serialized_request = encode_vec(&request).unwrap();
|
||||
|
||||
|
|
@ -338,6 +381,7 @@ fn to_buffer_correctly_serializes_server_license_request() {
|
|||
#[test]
|
||||
fn buffer_length_is_correct_for_server_license_request() {
|
||||
let request_buffer = [
|
||||
&LICENSE_HEADER_BUFFER_WITH_CERT[..],
|
||||
&SERVER_RANDOM_BUFFER[..],
|
||||
&PRODUCT_INFO_BUFFER[..],
|
||||
&KEY_EXCHANGE_LIST_BUFFER[..],
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
use super::{
|
||||
read_license_header, BlobHeader, BlobType, LicenseHeader, PreambleType, BLOB_LENGTH_SIZE, BLOB_TYPE_SIZE, MAC_SIZE,
|
||||
};
|
||||
use super::{BlobHeader, BlobType, LicenseHeader, PreambleType, BLOB_LENGTH_SIZE, BLOB_TYPE_SIZE, MAC_SIZE};
|
||||
use crate::{
|
||||
cursor::{ReadCursor, WriteCursor},
|
||||
PduDecode, PduEncode, PduResult,
|
||||
|
|
@ -27,31 +25,33 @@ impl ServerPlatformChallenge {
|
|||
const FIXED_PART_SIZE: usize = CONNECT_FLAGS_FIELD_SIZE + MAC_SIZE + BLOB_LENGTH_SIZE + BLOB_TYPE_SIZE;
|
||||
}
|
||||
|
||||
impl PduEncode for ServerPlatformChallenge {
|
||||
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
impl ServerPlatformChallenge {
|
||||
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
ensure_size!(in: dst, size: self.size());
|
||||
|
||||
self.license_header.encode(dst)?;
|
||||
dst.write_u32(0); // connect_flags, ignored
|
||||
BlobHeader::new(BlobType::Any, self.encrypted_platform_challenge.len()).encode(dst)?;
|
||||
BlobHeader::new(BlobType::ANY, self.encrypted_platform_challenge.len()).encode(dst)?;
|
||||
dst.write_slice(&self.encrypted_platform_challenge);
|
||||
dst.write_slice(&self.mac_data);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
pub fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
pub fn size(&self) -> usize {
|
||||
Self::FIXED_PART_SIZE + self.license_header.size() + self.encrypted_platform_challenge.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> PduDecode<'de> for ServerPlatformChallenge {
|
||||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
let license_header = read_license_header(PreambleType::PlatformChallenge, src)?;
|
||||
impl ServerPlatformChallenge {
|
||||
pub fn decode(license_header: LicenseHeader, src: &mut ReadCursor<'_>) -> PduResult<Self> {
|
||||
if license_header.preamble_message_type != PreambleType::PlatformChallenge {
|
||||
return Err(invalid_message_err!("preambleMessageType", "unexpected preamble type"));
|
||||
}
|
||||
|
||||
ensure_size!(in: src, size: 4);
|
||||
let _connect_flags = src.read_u32();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use lazy_static::lazy_static;
|
|||
|
||||
use super::*;
|
||||
use crate::rdp::server_license::{
|
||||
BasicSecurityHeader, BasicSecurityHeaderFlags, PreambleFlags, PreambleVersion, BASIC_SECURITY_HEADER_SIZE,
|
||||
BasicSecurityHeader, BasicSecurityHeaderFlags, LicensePdu, PreambleFlags, PreambleVersion,
|
||||
BASIC_SECURITY_HEADER_SIZE,
|
||||
};
|
||||
use crate::{decode, encode_vec};
|
||||
|
||||
|
|
@ -10,8 +11,8 @@ const PLATFORM_CHALLENGE_BUFFER: [u8; 42] = [
|
|||
0x80, 0x00, // flags
|
||||
0x00, 0x00, // flagsHi
|
||||
0x02, 0x03, 0x26, 0x00, // preamble
|
||||
0x00, 0x00, 0x00, 0x00, // connect flags
|
||||
0x00, 0x00, // ignored
|
||||
0x00, 0x00, 0x00, 0x00, // connect_flags (ignored)
|
||||
0x00, 0x00, // blob_type, ignored;
|
||||
0x0a, 0x00, // blob len
|
||||
0x46, 0x37, 0x85, 0x54, 0x8e, 0xc5, 0x91, 0x34, 0x97, 0x5d, // challenge
|
||||
0x38, 0x23, 0x62, 0x5d, 0x10, 0x8b, 0x93, 0xc3, 0xf1, 0xe4, 0x67, 0x1f, 0x4a, 0xb6, 0x00, 0x0a, // mac data
|
||||
|
|
@ -26,7 +27,7 @@ const MAC_DATA_BUFFER: [u8; MAC_SIZE] = [
|
|||
];
|
||||
|
||||
lazy_static! {
|
||||
pub static ref PLATFORM_CHALLENGE: ServerPlatformChallenge = ServerPlatformChallenge {
|
||||
pub static ref PLATFORM_CHALLENGE: LicensePdu = ServerPlatformChallenge {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
|
|
@ -38,7 +39,8 @@ lazy_static! {
|
|||
},
|
||||
encrypted_platform_challenge: Vec::from(CHALLENGE_BUFFER.as_ref()),
|
||||
mac_data: Vec::from(MAC_DATA_BUFFER.as_ref()),
|
||||
};
|
||||
}
|
||||
.into();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -46,6 +48,15 @@ fn from_buffer_correctly_parses_server_platform_challenge() {
|
|||
assert_eq!(*PLATFORM_CHALLENGE, decode(PLATFORM_CHALLENGE_BUFFER.as_ref()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_buffer_correctly_parses_server_platform_challenge_resiliently() {
|
||||
let mut buffer = PLATFORM_CHALLENGE_BUFFER;
|
||||
// Change blob type to junk value 0xbeef
|
||||
buffer[12] = 0xbe;
|
||||
buffer[13] = 0xef;
|
||||
assert_eq!(*PLATFORM_CHALLENGE, decode(buffer.as_ref()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_buffer_correctly_serializes_server_platform_challenge() {
|
||||
let serialized_platform_challenge = encode_vec(&*PLATFORM_CHALLENGE).unwrap();
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
mod tests;
|
||||
|
||||
use super::{
|
||||
read_license_header, BlobHeader, BlobType, LicenseEncryptionData, LicenseHeader, PreambleType, ServerLicenseError,
|
||||
BLOB_LENGTH_SIZE, BLOB_TYPE_SIZE, MAC_SIZE, UTF16_NULL_TERMINATOR_SIZE, UTF8_NULL_TERMINATOR_SIZE,
|
||||
BlobHeader, BlobType, LicenseEncryptionData, LicenseHeader, PreambleType, ServerLicenseError, BLOB_LENGTH_SIZE,
|
||||
BLOB_TYPE_SIZE, MAC_SIZE, UTF16_NULL_TERMINATOR_SIZE, UTF8_NULL_TERMINATOR_SIZE,
|
||||
};
|
||||
use crate::crypto::rc4::Rc4;
|
||||
use crate::cursor::{ReadCursor, WriteCursor};
|
||||
|
|
@ -41,31 +41,29 @@ impl ServerUpgradeLicense {
|
|||
const NAME: &'static str = "ServerUpgradeLicense";
|
||||
}
|
||||
|
||||
impl PduEncode for ServerUpgradeLicense {
|
||||
fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
impl ServerUpgradeLicense {
|
||||
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> {
|
||||
ensure_size!(in: dst, size: self.size());
|
||||
|
||||
self.license_header.encode(dst)?;
|
||||
BlobHeader::new(BlobType::EncryptedData, self.encrypted_license_info.len()).encode(dst)?;
|
||||
BlobHeader::new(BlobType::ENCRYPTED_DATA, self.encrypted_license_info.len()).encode(dst)?;
|
||||
dst.write_slice(&self.encrypted_license_info);
|
||||
dst.write_slice(&self.mac_data);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
pub fn name(&self) -> &'static str {
|
||||
Self::NAME
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
pub fn size(&self) -> usize {
|
||||
self.license_header.size() + BLOB_LENGTH_SIZE + BLOB_TYPE_SIZE + self.encrypted_license_info.len() + MAC_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> PduDecode<'de> for ServerUpgradeLicense {
|
||||
fn decode(src: &mut ReadCursor<'de>) -> PduResult<Self> {
|
||||
let license_header = read_license_header(PreambleType::NewLicense, src)?;
|
||||
|
||||
impl ServerUpgradeLicense {
|
||||
pub fn decode(license_header: LicenseHeader, src: &mut ReadCursor<'_>) -> PduResult<Self> {
|
||||
if license_header.preamble_message_type != PreambleType::UpgradeLicense
|
||||
&& license_header.preamble_message_type != PreambleType::NewLicense
|
||||
{
|
||||
|
|
@ -76,7 +74,7 @@ impl<'de> PduDecode<'de> for ServerUpgradeLicense {
|
|||
}
|
||||
|
||||
let encrypted_license_info_blob = BlobHeader::decode(src)?;
|
||||
if encrypted_license_info_blob.blob_type != BlobType::EncryptedData {
|
||||
if encrypted_license_info_blob.blob_type != BlobType::ENCRYPTED_DATA {
|
||||
return Err(invalid_message_err!("blobType", "unexpected blob type"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ use lazy_static::lazy_static;
|
|||
|
||||
use super::*;
|
||||
use crate::rdp::server_license::{
|
||||
BasicSecurityHeader, BasicSecurityHeaderFlags, PreambleFlags, PreambleVersion, BASIC_SECURITY_HEADER_SIZE,
|
||||
PREAMBLE_SIZE,
|
||||
BasicSecurityHeader, BasicSecurityHeaderFlags, LicensePdu, PreambleFlags, PreambleVersion,
|
||||
BASIC_SECURITY_HEADER_SIZE, PREAMBLE_SIZE,
|
||||
};
|
||||
use crate::{decode, encode_vec};
|
||||
|
||||
|
|
@ -252,7 +252,7 @@ lazy_static! {
|
|||
product_id: "A02".to_string(),
|
||||
license_info: Vec::from(&NEW_LICENSE_INFORMATION_BUFFER[NEW_LICENSE_INFORMATION_BUFFER.len() - 0x0799..]),
|
||||
};
|
||||
pub static ref SERVER_UPGRADE_LICENSE: ServerUpgradeLicense = ServerUpgradeLicense {
|
||||
pub static ref SERVER_UPGRADE_LICENSE: LicensePdu = ServerUpgradeLicense {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
|
|
@ -266,7 +266,8 @@ lazy_static! {
|
|||
&SERVER_UPGRADE_LICENSE_BUFFER[12..SERVER_UPGRADE_LICENSE_BUFFER.len() - MAC_SIZE]
|
||||
),
|
||||
mac_data: Vec::from(MAC_DATA.as_ref()),
|
||||
};
|
||||
}
|
||||
.into();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use lazy_static::lazy_static;
|
||||
|
||||
use super::*;
|
||||
use crate::{decode, encode_vec, PduErrorKind};
|
||||
use crate::{decode, encode_vec};
|
||||
|
||||
const LICENSE_HEADER_BUFFER: [u8; 8] = [
|
||||
0x80, 0x00, // flags
|
||||
|
|
@ -35,12 +35,6 @@ pub const STATUS_VALID_CLIENT_BUFFER: [u8; 20] = [
|
|||
0xff, 0x03, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
pub const UNEXPECTED_ERROR_BUFFER: [u8; 20] = [
|
||||
0x80, 0x00, // flags
|
||||
0x00, 0x00, // flagsHi
|
||||
0xff, 0x03, 0x10, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
lazy_static! {
|
||||
pub static ref LICENSE_HEADER: LicenseHeader = LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
|
|
@ -56,7 +50,7 @@ lazy_static! {
|
|||
#[test]
|
||||
fn read_blob_header_handles_wrong_type_correctly() {
|
||||
let h = decode::<BlobHeader>(&BLOB_BUFFER).unwrap();
|
||||
assert_ne!(h.blob_type, BlobType::Certificate);
|
||||
assert_ne!(h.blob_type, BlobType::CERTIFICATE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -71,16 +65,20 @@ fn read_blob_header_handles_invalid_type_correctly() {
|
|||
0x00, // blob data
|
||||
];
|
||||
|
||||
match decode::<BlobHeader>(&invalid_blob_buffer) {
|
||||
Err(e) if matches!(e.kind(), PduErrorKind::InvalidMessage { .. }) => (),
|
||||
_ => panic!("expected invalid message error"),
|
||||
}
|
||||
let header = decode::<BlobHeader>(&invalid_blob_buffer).unwrap();
|
||||
assert_eq!(
|
||||
header,
|
||||
BlobHeader {
|
||||
blob_type: BlobType(0x99),
|
||||
length: 0x48
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_blob_header_reads_blob_correctly() {
|
||||
let blob = decode::<BlobHeader>(&BLOB_BUFFER).unwrap();
|
||||
assert_eq!(blob.blob_type, BlobType::RsaSignature);
|
||||
assert_eq!(blob.blob_type, BlobType::RSA_SIGNATURE);
|
||||
assert_eq!(blob.length, BLOB_BUFFER.len() - 4);
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +87,7 @@ fn write_blob_header_writes_blob_header_correctly() {
|
|||
let correct_blob_header = &BLOB_BUFFER[..4];
|
||||
let blob_data = &BLOB_BUFFER[4..];
|
||||
|
||||
let blob = BlobHeader::new(BlobType::RsaSignature, blob_data.len());
|
||||
let blob = BlobHeader::new(BlobType::RSA_SIGNATURE, blob_data.len());
|
||||
let buffer = encode_vec(&blob).unwrap();
|
||||
|
||||
assert_eq!(correct_blob_header, buffer.as_slice());
|
||||
|
|
@ -135,21 +133,28 @@ fn buffer_length_is_correct_for_license_header() {
|
|||
|
||||
#[test]
|
||||
fn read_license_header_reads_correctly() {
|
||||
decode::<ServerPlatformChallenge>(&PLATFORM_CHALLENGE_BUFFER).unwrap();
|
||||
decode::<LicensePdu>(&PLATFORM_CHALLENGE_BUFFER).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_license_header_handles_valid_client_correctly() {
|
||||
match decode::<ServerPlatformChallenge>(&STATUS_VALID_CLIENT_BUFFER) {
|
||||
Err(e) if matches!(e.kind(), PduErrorKind::InvalidMessage { .. }) => (),
|
||||
e => panic!("The function has return an invalid error: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_license_header_handles_unexpected_error_correctly() {
|
||||
match decode::<ServerPlatformChallenge>(&UNEXPECTED_ERROR_BUFFER) {
|
||||
Err(e) if matches!(e.kind(), PduErrorKind::InvalidMessage { .. }) => (),
|
||||
e => panic!("The function has return an invalid error: {:?}", e),
|
||||
}
|
||||
let pdu = decode::<LicensePdu>(&STATUS_VALID_CLIENT_BUFFER).unwrap();
|
||||
assert_eq!(
|
||||
pdu,
|
||||
LicensingErrorMessage {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
},
|
||||
preamble_message_type: PreambleType::ErrorAlert,
|
||||
preamble_flags: PreambleFlags::empty(),
|
||||
preamble_version: PreambleVersion::V3,
|
||||
preamble_message_size: 0x10,
|
||||
},
|
||||
error_code: LicenseErrorCode::StatusValidClient,
|
||||
state_transition: LicensingStateTransition::NoTransition,
|
||||
error_info: Vec::new()
|
||||
}
|
||||
.into()
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ impl Processor {
|
|||
}
|
||||
_ => Err(reason_err!(
|
||||
"IO channel",
|
||||
"unexpected PDU: expected Session Save Info PDU, got: {:?}",
|
||||
"unhandled PDU: {:?}",
|
||||
ctx.pdu.as_short_name()
|
||||
)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,7 +142,10 @@ pub const SERVER_FONT_MAP_BUFFER: [u8; 26] = [
|
|||
pub const SERVER_LICENSE_BUFFER: [u8; 20] = [
|
||||
0x80, 0x00, // flags
|
||||
0x00, 0x00, // flagsHi
|
||||
0xff, 0x03, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0xff, // preamble_message_type
|
||||
0x03, // preamble_flags | preamble_version
|
||||
0x14, // preamble_message_size
|
||||
0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
lazy_static! {
|
||||
|
|
@ -152,21 +155,23 @@ lazy_static! {
|
|||
},
|
||||
client_info: CLIENT_INFO_UNICODE.clone(),
|
||||
};
|
||||
pub static ref SERVER_LICENSE_PDU: InitialServerLicenseMessage = InitialServerLicenseMessage {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
pub static ref SERVER_LICENSE_PDU: LicensePdu = {
|
||||
let mut pdu = LicensingErrorMessage {
|
||||
license_header: LicenseHeader {
|
||||
security_header: BasicSecurityHeader {
|
||||
flags: BasicSecurityHeaderFlags::LICENSE_PKT,
|
||||
},
|
||||
preamble_message_type: PreambleType::ErrorAlert,
|
||||
preamble_flags: PreambleFlags::empty(),
|
||||
preamble_version: PreambleVersion::V3,
|
||||
preamble_message_size: 0,
|
||||
},
|
||||
preamble_message_type: PreambleType::ErrorAlert,
|
||||
preamble_flags: PreambleFlags::empty(),
|
||||
preamble_version: PreambleVersion::V3,
|
||||
preamble_message_size: (SERVER_LICENSE_BUFFER.len() - BASIC_SECURITY_HEADER_SIZE) as u16
|
||||
},
|
||||
message_type: InitialMessageType::StatusValidClient(LicensingErrorMessage {
|
||||
error_code: LicenseErrorCode::StatusValidClient,
|
||||
state_transition: LicensingStateTransition::NoTransition,
|
||||
error_info: Vec::new(),
|
||||
})
|
||||
};
|
||||
pdu.license_header.preamble_message_size = pdu.size() as u16;
|
||||
pdu.into()
|
||||
};
|
||||
pub static ref SERVER_DEMAND_ACTIVE_PDU: ShareControlHeader = ShareControlHeader {
|
||||
share_control_pdu: ShareControlPdu::ServerDemandActive(SERVER_DEMAND_ACTIVE.clone()),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue