rdp/core/
sec.rs

1use core::mcs;
2use core::license;
3use core::tpkt;
4use model::error::{RdpResult, Error, RdpError, RdpErrorKind};
5use model::data::{Message, Component, U16, U32, DynOption, MessageOption, Trame, DataType};
6use std::io::{Write, Read};
7use model::unicode::Unicode;
8
9/// Security flag send as header flage in core ptotocol
10/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/e13405c5-668b-4716-94b2-1c2654ca1ad4?redirectedfrom=MSDN
11#[repr(u16)]
12#[allow(dead_code)]
13enum SecurityFlag {
14    SecExchangePkt = 0x0001,
15    SecTransportReq = 0x0002,
16    RdpSecTransportRsp = 0x0004,
17    SecEncrypt = 0x0008,
18    SecResetSeqno = 0x0010,
19    SecIgnoreSeqno = 0x0020,
20    SecInfoPkt = 0x0040,
21    SecLicensePkt = 0x0080,
22    SecLicenseEncryptCs = 0x0200,
23    SecRedirectionPkt = 0x0400,
24    SecSecureChecksum = 0x0800,
25    SecAutodetectReq = 0x1000,
26    SecAutodetectRsp = 0x2000,
27    SecHeartbeat = 0x4000,
28    SecFlagshiValid = 0x8000
29}
30
31/// RDP option someone links to capabilities
32/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/732394f5-e2b5-4ac5-8a0a-35345386b0d1?redirectedfrom=MSDN
33#[allow(dead_code)]
34enum InfoFlag {
35    InfoMouse = 0x00000001,
36    InfoDisablectrlaltdel = 0x00000002,
37    InfoAutologon = 0x00000008,
38    InfoUnicode = 0x00000010,
39    InfoMaximizeshell = 0x00000020,
40    InfoLogonnotify = 0x00000040,
41    InfoCompression = 0x00000080,
42    InfoEnablewindowskey = 0x00000100,
43    InfoRemoteconsoleaudio = 0x00002000,
44    InfoForceEncryptedCsPdu = 0x00004000,
45    InfoRail = 0x00008000,
46    InfoLogonerrors = 0x00010000,
47    InfoMouseHasWheel = 0x00020000,
48    InfoPasswordIsScPin = 0x00040000,
49    InfoNoaudioplayback = 0x00080000,
50    InfoUsingSavedCreds = 0x00100000,
51    InfoAudiocapture = 0x00200000,
52    InfoVideoDisable = 0x00400000,
53    InfoCompressionTypeMask = 0x00001E00
54}
55
56#[allow(dead_code)]
57enum AfInet {
58    AfInet = 0x00002,
59    AfInet6 = 0x0017
60}
61
62/// On RDP version > 5
63/// Client have to send IP information
64fn rdp_extended_infos() -> Component {
65    component![
66        "clientAddressFamily" => U16::LE(AfInet::AfInet as u16),
67        "cbClientAddress" => DynOption::new(U16::LE(0), |x| MessageOption::Size("clientAddress".to_string(), x.inner() as usize + 2)),
68        "clientAddress" => b"\x00\x00".to_vec(),
69        "cbClientDir" => U16::LE(0),
70        "clientDir" => b"\x00\x00".to_vec(),
71        "clientTimeZone" => vec![0; 172],
72        "clientSessionId" => U32::LE(0),
73        "performanceFlags" => U32::LE(0)
74    ]
75}
76
77/// When CSSP is not used
78/// interactive logon used credentials
79/// present in this payload
80fn rdp_infos(is_extended_info: bool, domain: &String, username: &String, password: &String, auto_logon: bool) -> Component {
81    let mut domain_format = domain.to_unicode();
82    domain_format.push(0);
83    domain_format.push(0);
84
85    let mut username_format = username.to_unicode();
86    username_format.push(0);
87    username_format.push(0);
88
89    let mut password_format = password.to_unicode();
90    password_format.push(0);
91    password_format.push(0);
92
93    component![
94        "codePage" => U32::LE(0),
95        "flag" => U32::LE(
96            InfoFlag::InfoMouse as u32 |
97            InfoFlag::InfoUnicode as u32 |
98            InfoFlag::InfoLogonnotify as u32 |
99            InfoFlag::InfoLogonerrors as u32 |
100            InfoFlag::InfoDisablectrlaltdel as u32 |
101            InfoFlag::InfoEnablewindowskey as u32 |
102            if auto_logon { InfoFlag::InfoAutologon as u32 } else { 0 }
103        ),
104        "cbDomain" => U16::LE((domain_format.len() - 2) as u16),
105        "cbUserName" => U16::LE((username_format.len() - 2) as u16),
106        "cbPassword" => U16::LE((password_format.len() - 2) as u16),
107        "cbAlternateShell" => U16::LE(0),
108        "cbWorkingDir" => U16::LE(0),
109        "domain" => domain_format,
110        "userName" => username_format,
111        "password" => password_format,
112        "alternateShell" => b"\x00\x00".to_vec(),
113        "workingDir" => b"\x00\x00".to_vec(),
114        "extendedInfos" => if is_extended_info { rdp_extended_infos() } else { component![] }
115    ]
116}
117
118/// Details of the security header
119fn security_header() -> Component {
120    component![
121        "securityFlag" => U16::LE(0),
122        "securityFlagHi" => U16::LE(0)
123    ]
124}
125
126
127/// Security layer need mcs layer and send all message through
128/// the global channel
129///
130/// This function is called sec because old RDP security
131/// was made here
132///
133/// # Example
134/// ```rust, ignore
135/// use rdp::core::sec;
136/// let mut mcs = mcs::Client(...).unwrap();
137/// sec::connect(&mut mcs).unwrap();
138/// ```
139pub fn connect<T: Read + Write>(mcs: &mut mcs::Client<T>, domain: &String, username: &String, password: &String, auto_logon: bool) -> RdpResult<()> {
140    mcs.write(
141        &"global".to_string(),
142        trame![
143            U16::LE(SecurityFlag::SecInfoPkt as u16),
144            U16::LE(0),
145            rdp_infos(
146                mcs.is_rdp_version_5_plus(),
147                domain,
148                username,
149                password,
150                auto_logon
151            )
152        ]
153    )?;
154
155    let (_channel_name, payload) = mcs.read()?;
156    let mut stream = try_let!(tpkt::Payload::Raw, payload)?;
157    let mut header = security_header();
158    header.read(&mut stream)?;
159    if cast!(DataType::U16, header["securityFlag"])? & SecurityFlag::SecLicensePkt as u16 == 0 {
160        return Err(Error::RdpError(RdpError::new(RdpErrorKind::InvalidData, "SEC: Invalid Licence packet")));
161    }
162
163    license::client_connect(&mut stream)?;
164    Ok(())
165}
166
167