use nla::asn1::{ASN1, Sequence, ExplicitTag, SequenceOf, ASN1Type, OctetString, Integer, to_der};
use model::error::{RdpError, RdpErrorKind, Error, RdpResult};
use num_bigint::{BigUint};
use yasna::Tag;
use x509_parser::{parse_x509_der, X509Certificate};
use nla::sspi::AuthenticationProtocol;
use model::link::Link;
use std::io::{Read, Write};
pub fn create_ts_request(nego: Vec<u8>) -> Vec<u8> {
let ts_request = sequence![
"version" => ExplicitTag::new(Tag::context(0), 2 as Integer),
"negoTokens" => ExplicitTag::new(Tag::context(1),
sequence_of![
sequence![
"negoToken" => ExplicitTag::new(Tag::context(0), nego)
]
])
];
to_der(&ts_request)
}
pub fn read_ts_server_challenge(stream: &[u8]) -> RdpResult<Vec<u8>> {
let mut ts_request = sequence![
"version" => ExplicitTag::new(Tag::context(0), 2 as Integer),
"negoTokens" => ExplicitTag::new(Tag::context(1),
SequenceOf::reader(|| {
Box::new(sequence![
"negoToken" => ExplicitTag::new(Tag::context(0), OctetString::new())
])
})
)
];
yasna::parse_der(stream, |reader| {
if let Err(Error::ASN1Error(e)) = ts_request.read_asn1(reader) {
return Err(e)
}
Ok(())
})?;
let nego_tokens = cast!(ASN1Type::SequenceOf, ts_request["negoTokens"]).unwrap();
let first_nego_tokens = cast!(ASN1Type::Sequence, nego_tokens.inner[0]).unwrap();
let nego_token = cast!(ASN1Type::OctetString, first_nego_tokens["negoToken"]).unwrap();
Ok(nego_token.to_vec())
}
pub fn create_ts_authenticate(nego: Vec<u8>, pub_key_auth: Vec<u8>) -> Vec<u8> {
let ts_challenge = sequence![
"version" => ExplicitTag::new(Tag::context(0), 2 as Integer),
"negoTokens" => ExplicitTag::new(Tag::context(1),
sequence_of![
sequence![
"negoToken" => ExplicitTag::new(Tag::context(0), nego as OctetString)
]
]),
"pubKeyAuth" => ExplicitTag::new(Tag::context(3), pub_key_auth as OctetString)
];
to_der(&ts_challenge)
}
pub fn read_public_certificate(stream: &[u8]) -> RdpResult<X509Certificate> {
let res = parse_x509_der(stream).unwrap();
Ok(res.1)
}
pub fn read_ts_validate(request: &[u8]) -> RdpResult<Vec<u8>> {
let mut ts_challenge = sequence![
"version" => ExplicitTag::new(Tag::context(0), 2 as Integer),
"pubKeyAuth" => ExplicitTag::new(Tag::context(3), OctetString::new())
];
yasna::parse_der(request, |reader| {
if let Err(Error::ASN1Error(e)) = ts_challenge.read_asn1(reader) {
return Err(e)
}
Ok(())
})?;
let pubkey = cast!(ASN1Type::OctetString, ts_challenge["pubKeyAuth"])?;
Ok(pubkey.to_vec())
}
fn create_ts_credentials(domain: Vec<u8>, user: Vec<u8>, password: Vec<u8>) -> Vec<u8> {
let ts_password_creds = sequence![
"domainName" => ExplicitTag::new(Tag::context(0), domain as OctetString),
"userName" => ExplicitTag::new(Tag::context(1), user as OctetString),
"password" => ExplicitTag::new(Tag::context(2), password as OctetString)
];
let ts_password_cred_encoded = yasna::construct_der(|writer| {
ts_password_creds.write_asn1(writer).unwrap();
});
let ts_credentials = sequence![
"credType" => ExplicitTag::new(Tag::context(0), 1 as Integer),
"credentials" => ExplicitTag::new(Tag::context(1), ts_password_cred_encoded as OctetString)
];
to_der(&ts_credentials)
}
fn create_ts_authinfo(auth_info: Vec<u8>) -> Vec<u8> {
let ts_authinfo = sequence![
"version" => ExplicitTag::new(Tag::context(0), 2 as Integer),
"authInfo" => ExplicitTag::new(Tag::context(2), auth_info)
];
to_der(&ts_authinfo)
}
pub fn cssp_connect<S: Read + Write>(link: &mut Link<S>, authentication_protocol: &mut dyn AuthenticationProtocol, restricted_admin_mode: bool) -> RdpResult<()> {
let negotiate_message = create_ts_request(authentication_protocol.create_negotiate_message()?);
link.write(&negotiate_message)?;
let server_challenge = read_ts_server_challenge(&(link.read(0)?))?;
let client_challenge = authentication_protocol.read_challenge_message(&server_challenge)?;
let mut security_interface = authentication_protocol.build_security_interface();
let certificate_der = try_option!(link.get_peer_certificate()?, "No public certificate available")?.to_der()?;
let certificate = read_public_certificate(&certificate_der)?;
let challenge = create_ts_authenticate(client_challenge, security_interface.gss_wrapex(certificate.tbs_certificate.subject_pki.subject_public_key.data)?);
link.write(&challenge)?;
let inc_pub_key = security_interface.gss_unwrapex(&(read_ts_validate(&(link.read(0)?))?))?;
if BigUint::from_bytes_le(&inc_pub_key) != BigUint::from_bytes_le(certificate.tbs_certificate.subject_pki.subject_public_key.data) + BigUint::new(vec![1]) {
return Err(Error::RdpError(RdpError::new(RdpErrorKind::PossibleMITM, "Man in the middle detected")))
}
let domain = if restricted_admin_mode { vec![] } else { authentication_protocol.get_domain_name()};
let user = if restricted_admin_mode { vec![] } else { authentication_protocol.get_user_name() };
let password = if restricted_admin_mode { vec![] } else { authentication_protocol.get_password() };
let credentials = create_ts_authinfo(security_interface.gss_wrapex(&create_ts_credentials(domain, user, password))?);
link.write(&credentials)?;
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_create_ts_credentials() {
let credentials = create_ts_credentials(b"domain".to_vec(), b"user".to_vec(), b"password".to_vec());
let result = [48, 41, 160, 3, 2, 1, 1, 161, 34, 4, 32, 48, 30, 160, 8, 4, 6, 100, 111, 109, 97, 105, 110, 161, 6, 4, 4, 117, 115, 101, 114, 162, 10, 4, 8, 112, 97, 115, 115, 119, 111, 114, 100];
assert_eq!(credentials[0..32], result[0..32]);
assert_eq!(credentials[33..43], result[33..43]);
}
#[test]
fn test_create_ts_authinfo() {
assert_eq!(create_ts_authinfo(b"foo".to_vec()), [48, 12, 160, 3, 2, 1, 2, 162, 5, 4, 3, 102, 111, 111])
}
}