mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2025-03-09 15:40:18 +00:00
parent
f88d3063fe
commit
db06ec1975
37 changed files with 28174 additions and 44 deletions
175
rdp/protocol/cert.js
Normal file
175
rdp/protocol/cert.js
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../core').type;
|
||||
var log = require('../core').log;
|
||||
var x509 = require('../security').x509;
|
||||
var rsa = require('../security').rsa;
|
||||
var asn1 = require('../asn1');
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240521.aspx
|
||||
*/
|
||||
var CertificateType = {
|
||||
CERT_CHAIN_VERSION_1 : 0x00000001,
|
||||
CERT_CHAIN_VERSION_2 : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240520.aspx
|
||||
* @returns
|
||||
*/
|
||||
function rsaPublicKey(opt) {
|
||||
var self = {
|
||||
magic : new type.UInt32Le(0x31415352, { constant : true }),
|
||||
keylen : new type.UInt32Le(function() {
|
||||
return self.modulus.size() + self.paddinf.size();
|
||||
}),
|
||||
bitlen : new type.UInt32Le(function() {
|
||||
return (self.keylen.value - 8) * 8;
|
||||
}),
|
||||
datalen : new type.UInt32Le(function() {
|
||||
return (self.bitlen.value / 8) - 1;
|
||||
}),
|
||||
pubExp : new type.UInt32Le(),
|
||||
modulus : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return self.keylen.value - 8;
|
||||
}) }),
|
||||
padding : new type.BinaryString(Buffer.from(Array(8 + 1).join('\x00')), { readLength : new type.CallableValue(8) })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* http://msdn.microsoft.com/en-us/library/cc240519.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function proprietaryCertificate() {
|
||||
var self = {
|
||||
__TYPE__ : CertificateType.CERT_CHAIN_VERSION_1,
|
||||
dwSigAlgId : new type.UInt32Le(0x00000001, { constant : true }),
|
||||
dwKeyAlgId : new type.UInt32Le(0x00000001, { constant : true }),
|
||||
wPublicKeyBlobType : new type.UInt16Le(0x0006, { constant : true }),
|
||||
wPublicKeyBlobLen : new type.UInt16Le(function() {
|
||||
return self.PublicKeyBlob.size();
|
||||
}),
|
||||
PublicKeyBlob : rsaPublicKey({ readLength : new type.CallableValue(function() {
|
||||
return self.wPublicKeyBlobLen.value;
|
||||
}) }),
|
||||
wSignatureBlobType : new type.UInt16Le(0x0008, { constant : true }),
|
||||
wSignatureBlobLen : new type.UInt16Le(function() {
|
||||
return self.SignatureBlob.size() + self.padding.size();
|
||||
}),
|
||||
SignatureBlob : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return self.wSignatureBlobLen.value - self.padding.size;
|
||||
}) }),
|
||||
padding : new type.BinaryString(Array(8 + 1).join('\x00'), { readLength : new type.CallableValue(8) }),
|
||||
/**
|
||||
* @return {object} rsa.publicKey
|
||||
*/
|
||||
getPublicKey : function() {
|
||||
return rsa.publicKey(self.PublicKeyBlob.obj.modulus.value, self.PublicKeyBlob.obj.pubExp.value);
|
||||
}
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* For x509 certificate
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241911.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function certBlob() {
|
||||
var self = {
|
||||
cbCert : new type.UInt32Le(function() {
|
||||
return self.abCert.size();
|
||||
}),
|
||||
abCert : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return self.cbCert.value;
|
||||
}) })
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* x509 certificate chain
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241910.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function x509CertificateChain() {
|
||||
var self = {
|
||||
__TYPE__ : CertificateType.CERT_CHAIN_VERSION_2,
|
||||
NumCertBlobs : new type.UInt32Le(),
|
||||
CertBlobArray : new type.Factory(function(s) {
|
||||
self.CertBlobArray = new type.Component([]);
|
||||
for(var i = 0; i < self.NumCertBlobs.value; i++) {
|
||||
self.CertBlobArray.obj.push(certBlob().read(s));
|
||||
}
|
||||
}),
|
||||
padding : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return 8 + 4 * self.NumCertBlobs.value;
|
||||
}) }),
|
||||
/**
|
||||
* @return {object} {n : modulus{bignum}, e : publicexponent{integer}
|
||||
*/
|
||||
getPublicKey : function(){
|
||||
var cert = x509.X509Certificate().decode(new type.Stream(self.CertBlobArray.obj[self.CertBlobArray.obj.length - 1].obj.abCert.value), asn1.ber);
|
||||
var publikeyStream = new type.Stream(cert.value.tbsCertificate.value.subjectPublicKeyInfo.value.subjectPublicKey.toBuffer());
|
||||
var asn1PublicKey = x509.RSAPublicKey().decode(publikeyStream, asn1.ber);
|
||||
return rsa.publicKey(asn1PublicKey.value.modulus.value, asn1PublicKey.value.publicExponent.value);
|
||||
}
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
function certificate() {
|
||||
var self = {
|
||||
dwVersion : new type.UInt32Le(function() {
|
||||
return self.certData.__TYPE__;
|
||||
}),
|
||||
certData : new type.Factory(function(s) {
|
||||
switch(self.dwVersion.value & 0x7fffffff) {
|
||||
case CertificateType.CERT_CHAIN_VERSION_1:
|
||||
log.debug('read proprietary certificate');
|
||||
self.certData = proprietaryCertificate().read(s);
|
||||
break;
|
||||
case CertificateType.CERT_CHAIN_VERSION_2:
|
||||
log.debug('read x.509 certificate chain');
|
||||
self.certData = x509CertificateChain().read(s);
|
||||
break;
|
||||
default:
|
||||
log.error('unknown cert type ' + self.dwVersion.value & 0x7fffffff);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
CertificateType : CertificateType,
|
||||
certificate : certificate
|
||||
};
|
||||
30
rdp/protocol/index.js
Normal file
30
rdp/protocol/index.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var TPKT = require('./tpkt');
|
||||
var x224 = require('./x224');
|
||||
var t125 = require('./t125');
|
||||
var rdp = require('./rdp');
|
||||
|
||||
module.exports = {
|
||||
TPKT : TPKT,
|
||||
x224 : x224,
|
||||
t125 : t125,
|
||||
rdp : rdp
|
||||
};
|
||||
701
rdp/protocol/nla.js
Normal file
701
rdp/protocol/nla.js
Normal file
|
|
@ -0,0 +1,701 @@
|
|||
const inherits = require('util').inherits;
|
||||
const type = require('../core').type;
|
||||
const events = require('events');
|
||||
const crypto = require('crypto');
|
||||
const forge = require('node-forge');
|
||||
const asn1 = forge.asn1;
|
||||
const pki = forge.pki;
|
||||
|
||||
/**
|
||||
* NLA layer of rdp stack
|
||||
*/
|
||||
function NLA(transport, nlaCompletedFunc, domain, user, password) {
|
||||
// Get NTLM ready
|
||||
const ntlm = Create_Ntlm();
|
||||
ntlm.domain = domain;
|
||||
ntlm.completedFunc = nlaCompletedFunc;
|
||||
ntlm.user = user;
|
||||
ntlm.password = password;
|
||||
ntlm.response_key_nt = ntowfv2(ntlm.password, ntlm.user, ntlm.domain);
|
||||
ntlm.response_key_lm = lmowfv2(ntlm.password, ntlm.user, ntlm.domain);
|
||||
this.ntlm = ntlm;
|
||||
this.state = 1;
|
||||
|
||||
// Get transport ready
|
||||
this.transport = transport;
|
||||
// Wait 2 bytes
|
||||
this.transport.expect(2);
|
||||
// Next state is receive header
|
||||
var self = this;
|
||||
|
||||
this.oldDataListeners = this.transport.listeners('data');
|
||||
this.oldCloseListeners = this.transport.listeners('close');
|
||||
this.oldErrorListeners = this.transport.listeners('error');
|
||||
|
||||
// Unhook the previous transport handler
|
||||
this.transport.removeAllListeners('data');
|
||||
this.transport.removeAllListeners('close');
|
||||
this.transport.removeAllListeners('error');
|
||||
|
||||
// Hook this module as the transport handler
|
||||
this.transport.once('data', function (s) {
|
||||
self.recvHeader(s);
|
||||
}).on('close', function () {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('close'); // Errors occur when NLA authentication fails, for now, just close.
|
||||
//self.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* inherit from a packet layer
|
||||
*/
|
||||
inherits(NLA, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Receive correct packet as expected
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
NLA.prototype.recvHeader = function (s) {
|
||||
//console.log('NLA - recvHeader', s);
|
||||
var self = this;
|
||||
var derType = new type.UInt8().read(s).value;
|
||||
var derLen = new type.UInt8().read(s).value;
|
||||
self.buffers = [ s.buffer ];
|
||||
|
||||
if (derLen < 128) {
|
||||
// wait for the entire data block
|
||||
this.transport.expect(derLen);
|
||||
this.transport.once('data', function (s) { self.recvData(s); });
|
||||
} else {
|
||||
// wait for the header size
|
||||
this.transport.expect(derLen - 128);
|
||||
this.transport.once('data', function (s) { self.recvHeaderSize(s); });
|
||||
}
|
||||
|
||||
//console.log('NLA - DER', derType, derLen);
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive correct packet as expected
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
NLA.prototype.recvHeaderSize = function (s) {
|
||||
//console.log('NLA - recvHeaderSize', s.buffer.length);
|
||||
var self = this;
|
||||
self.buffers.push(s.buffer);
|
||||
if (s.buffer.length == 1) {
|
||||
// wait for the entire data block
|
||||
var derLen = s.buffer.readUInt8(0);
|
||||
this.transport.expect(derLen);
|
||||
this.transport.once('data', function (s) { self.recvData(s); });
|
||||
} else if (s.buffer.length == 2) {
|
||||
// wait for the entire data block
|
||||
var derLen = s.buffer.readUInt16BE(0);
|
||||
this.transport.expect(derLen);
|
||||
this.transport.once('data', function (s) { self.recvData(s); });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive correct packet as expected
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
NLA.prototype.recvData = function (s) {
|
||||
//console.log('NLA - recvData', s.buffer.length);
|
||||
var self = this;
|
||||
self.buffers.push(s.buffer);
|
||||
var entireBuffer = Buffer.concat(self.buffers);
|
||||
//console.log('entireBuffer', entireBuffer.toString('hex'));
|
||||
|
||||
// We have a full ASN1 data block, decode it now
|
||||
const der = asn1.fromDer(entireBuffer.toString('binary'));
|
||||
const derNum = der.value[0].value[0].value.charCodeAt(0);
|
||||
//console.log('NLA - Number', derNum);
|
||||
|
||||
if (derNum == 6) {
|
||||
if (this.state == 1) {
|
||||
const derBuffer = Buffer.from(der.value[1].value[0].value[0].value[0].value[0].value, 'binary');
|
||||
const client_challenge = read_challenge_message(this.ntlm, derBuffer);
|
||||
self.security_interface = build_security_interface(this.ntlm);
|
||||
const peer_cert = this.transport.secureSocket.getPeerCertificate();
|
||||
const challenge = create_ts_authenticate(client_challenge, self.security_interface.gss_wrapex(peer_cert.pubkey.slice(24)));
|
||||
this.ntlm.publicKeyDer = peer_cert.pubkey.slice(24);
|
||||
this.send(challenge);
|
||||
this.state = 2;
|
||||
} else if (this.state == 2) {
|
||||
const derBuffer = Buffer.from(der.value[1].value[0].value, 'binary');
|
||||
const publicKeyDer = self.security_interface.gss_unwrapex(derBuffer);
|
||||
|
||||
// Check that the public key is identical except the first byte which is the DER encoding type.
|
||||
if (!this.ntlm.publicKeyDer.slice(1).equals(publicKeyDer.slice(1))) { console.log('RDP man-in-the-middle detected.'); close(); return; }
|
||||
delete this.ntlm.publicKeyDer; // Clean this up, we don't need it anymore.
|
||||
|
||||
var xdomain, xuser, xpassword;
|
||||
if (this.ntlm.is_unicode) {
|
||||
xdomain = toUnicode(this.ntlm.domain);
|
||||
xuser = toUnicode(this.ntlm.user);
|
||||
xpassword = toUnicode(this.ntlm.password);
|
||||
} else {
|
||||
xdomain = Buffer.from(this.ntlm.domain, 'utf8');
|
||||
xuser = Buffer.from(this.ntlm.user, 'utf8');
|
||||
xpassword = Buffer.from(this.ntlm.password, 'utf8');
|
||||
}
|
||||
|
||||
const credentials = create_ts_authinfo(self.security_interface.gss_wrapex(create_ts_credentials(xdomain, xuser, xpassword)));
|
||||
this.send(credentials);
|
||||
|
||||
// Rehook the previous transport handler
|
||||
this.transport.removeAllListeners('data');
|
||||
this.transport.removeAllListeners('close');
|
||||
this.transport.removeAllListeners('error');
|
||||
|
||||
for (var i in this.oldDataListeners) { this.transport.once('data', this.oldDataListeners[i]); }
|
||||
for (var i in this.oldCloseListeners) { this.transport.on('close', this.oldCloseListeners[i]); }
|
||||
for (var i in this.oldErrorListeners) { this.transport.on('error', this.oldErrorListeners[i]); }
|
||||
|
||||
// Done!
|
||||
this.transport.expect(2);
|
||||
this.state = 3;
|
||||
this.ntlm.completedFunc();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Receive next block of data
|
||||
this.transport.expect(2);
|
||||
this.transport.once('data', function (s) { self.recvHeader(s); });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send message throught NLA layer
|
||||
* @param message {type.*}
|
||||
*/
|
||||
NLA.prototype.send = function (message) {
|
||||
this.transport.sendBuffer(message);
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
NLA.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
|
||||
NLA.prototype.sendNegotiateMessage = function () {
|
||||
// Create create_ts_request
|
||||
this.ntlm.negotiate_message = create_negotiate_message();
|
||||
const asn1obj =
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, this.ntlm.negotiate_message.toString('binary'))
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
// Serialize an ASN.1 object to DER format
|
||||
this.send(Buffer.from(asn1.toDer(asn1obj).data, 'binary'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = NLA;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const NegotiateFlags = {
|
||||
NtlmsspNegociate56: 0x80000000,
|
||||
NtlmsspNegociateKeyExch: 0x40000000,
|
||||
NtlmsspNegociate128: 0x20000000,
|
||||
NtlmsspNegociateVersion: 0x02000000,
|
||||
NtlmsspNegociateTargetInfo: 0x00800000,
|
||||
NtlmsspRequestNonNTSessionKey: 0x00400000,
|
||||
NtlmsspNegociateIdentify: 0x00100000,
|
||||
NtlmsspNegociateExtendedSessionSecurity: 0x00080000,
|
||||
NtlmsspTargetTypeServer: 0x00020000,
|
||||
NtlmsspTargetTypeDomain: 0x00010000,
|
||||
NtlmsspNegociateAlwaysSign: 0x00008000,
|
||||
NtlmsspNegociateOEMWorkstationSupplied: 0x00002000,
|
||||
NtlmsspNegociateOEMDomainSupplied: 0x00001000,
|
||||
NtlmsspNegociateNTLM: 0x00000200,
|
||||
NtlmsspNegociateLMKey: 0x00000080,
|
||||
NtlmsspNegociateDatagram: 0x00000040,
|
||||
NtlmsspNegociateSeal: 0x00000020,
|
||||
NtlmsspNegociateSign: 0x00000010,
|
||||
NtlmsspRequestTarget: 0x00000004,
|
||||
NtlmNegotiateOEM: 0x00000002,
|
||||
NtlmsspNegociateUnicode: 0x00000001
|
||||
}
|
||||
|
||||
const MajorVersion = {
|
||||
WindowsMajorVersion5: 0x05,
|
||||
WindowsMajorVersion6: 0x06
|
||||
}
|
||||
|
||||
const MinorVersion = {
|
||||
WindowsMinorVersion0: 0x00,
|
||||
WindowsMinorVersion1: 0x01,
|
||||
WindowsMinorVersion2: 0x02,
|
||||
WindowsMinorVersion3: 0x03
|
||||
}
|
||||
|
||||
const NTLMRevision = {
|
||||
NtlmSspRevisionW2K3: 0x0F
|
||||
}
|
||||
|
||||
function decodeTargetInfo(targetInfoBuf) {
|
||||
var r = {}, type, len, data, ptr = 0;
|
||||
while (true) {
|
||||
type = targetInfoBuf.readInt16LE(ptr);
|
||||
if (type == 0) break;
|
||||
len = targetInfoBuf.readInt16LE(ptr + 2);
|
||||
r[type] = targetInfoBuf.slice(ptr + 4, ptr + 4 + len);
|
||||
ptr += (4 + len);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
function bufToArr(b) { var r = []; for (var i = 0; i < b.length; i++) { r.push(b.readUInt8(i)); } return r; } // For unit testing
|
||||
function compareArray(a, b) { if (a.length != b.length) return false; for (var i = 0; i < a.length; i++) { if (a[i] != b[i]) return false; } return true; } // For unit testing
|
||||
function toUnicode(str) { return Buffer.from(str, 'ucs2'); }
|
||||
function md4(str) { return crypto.createHash('md4').update(str).digest(); }
|
||||
function md5(str) { return crypto.createHash('md5').update(str).digest(); }
|
||||
function hmac_md5(key, data) { return crypto.createHmac('md5', key).update(data).digest(); }
|
||||
function ntowfv2(password, user, domain) { return hmac_md5(md4(toUnicode(password)), toUnicode(user.toUpperCase() + domain)); }
|
||||
function lmowfv2(password, user, domain) { return ntowfv2(password, user, domain); }
|
||||
function zeroBuffer(len) { return Buffer.alloc(len); }
|
||||
function compute_response_v2(response_key_nt, response_key_lm, server_challenge, client_challenge, time, server_name) {
|
||||
const response_version = Buffer.from('01', 'hex');
|
||||
const hi_response_version = Buffer.from('01', 'hex');
|
||||
const temp = Buffer.concat([response_version, hi_response_version, zeroBuffer(6), time, client_challenge, zeroBuffer(4), server_name]);
|
||||
const nt_proof_str = hmac_md5(response_key_nt, Buffer.concat([server_challenge, temp]));
|
||||
const nt_challenge_response = Buffer.concat([nt_proof_str, temp]);
|
||||
const lm_challenge_response = Buffer.concat([hmac_md5(response_key_lm, Buffer.concat([server_challenge, client_challenge])), client_challenge]);
|
||||
const session_base_key = hmac_md5(response_key_nt, nt_proof_str);
|
||||
return [nt_challenge_response, lm_challenge_response, session_base_key];
|
||||
}
|
||||
function kx_key_v2(session_base_key, _lm_challenge_response, _server_challenge) { return session_base_key; }
|
||||
function rc4k(key, data) { return crypto.createCipheriv('rc4', key, null).update(data); }
|
||||
|
||||
function create_negotiate_message() {
|
||||
return negotiate_message(
|
||||
NegotiateFlags.NtlmsspNegociateKeyExch |
|
||||
NegotiateFlags.NtlmsspNegociate128 |
|
||||
NegotiateFlags.NtlmsspNegociateExtendedSessionSecurity |
|
||||
NegotiateFlags.NtlmsspNegociateAlwaysSign |
|
||||
NegotiateFlags.NtlmsspNegociateNTLM |
|
||||
NegotiateFlags.NtlmsspNegociateSeal |
|
||||
NegotiateFlags.NtlmsspNegociateSign |
|
||||
NegotiateFlags.NtlmsspRequestTarget |
|
||||
NegotiateFlags.NtlmsspNegociateUnicode, Buffer.alloc(0), Buffer.alloc(0)
|
||||
);
|
||||
}
|
||||
|
||||
function negotiate_message(flags, domain, workstation) {
|
||||
const offset = ((flags & NegotiateFlags.NtlmsspNegociateVersion) == 0) ? 32 : 40;
|
||||
const buf = Buffer.alloc(offset);
|
||||
buf.write('4e544c4d53535000', 0, 8, 'hex'); // Signature (NTLMSP\0)
|
||||
buf.writeInt32LE(1, 8); // MessageType (1)
|
||||
buf.writeInt32LE(flags, 12); // Flags
|
||||
buf.writeInt16LE(domain.length, 16); // DomainNameLen
|
||||
buf.writeInt16LE(domain.length, 18); // DomainNameMaxLen
|
||||
if (domain.length > 0) { buf.writeInt32LE(offset, 20); } // DomainNameBufferOffset
|
||||
buf.writeInt16LE(workstation.length, 24); // WorkstationLen
|
||||
buf.writeInt16LE(workstation.length, 26); // WorkstationMaxLen
|
||||
if (workstation.length > 0) { buf.writeInt32LE(offset + domain.length, 28); } // WorkstationBufferOffset
|
||||
if ((flags & NegotiateFlags.NtlmsspNegociateVersion) != 0) {
|
||||
buf.writeUInt8(MajorVersion.WindowsMajorVersion6, 32); // ProductMajorVersion
|
||||
buf.writeUInt8(MinorVersion.WindowsMinorVersion0, 33); // ProductMinorVersion
|
||||
buf.writeInt16LE(6002, 34); // ProductBuild
|
||||
//buf.writeInt16LE(0, 36); // Reserved
|
||||
//buf.writeUInt8(0, 38); // Reserved
|
||||
buf.writeUInt8(NTLMRevision.NtlmSspRevisionW2K3, 39); // NTLMRevisionCurrent
|
||||
}
|
||||
return Buffer.concat([buf, domain, workstation]);
|
||||
}
|
||||
|
||||
function mac(rc4_handle, signing_key, seq_num, data) {
|
||||
const buf = Buffer.alloc(4);
|
||||
buf.writeInt32LE(seq_num, 0);
|
||||
var signature = hmac_md5(signing_key, Buffer.concat([buf, data]));
|
||||
return message_signature_ex(rc4_handle.update(signature.slice(0, 8)), seq_num);
|
||||
}
|
||||
|
||||
function message_signature_ex(check_sum, seq_num) {
|
||||
const buf = Buffer.alloc(16);
|
||||
buf.writeInt32LE(1, 0); // Version
|
||||
if (check_sum) { check_sum.copy(buf, 4, 0, 8); } // check_sum
|
||||
if (seq_num) { buf.writeInt32LE(seq_num, 12); } // seq_num
|
||||
return buf;
|
||||
}
|
||||
|
||||
/// Compute a signature of all data exchange during NTLMv2 handshake
|
||||
function mic(exported_session_key, negotiate_message, challenge_message, authenticate_message) { return hmac_md5(exported_session_key, Buffer.concat([negotiate_message, challenge_message, authenticate_message])); }
|
||||
|
||||
/// NTLMv2 security interface generate a sign key
|
||||
/// By using MD5 of the session key + a static member (sentense)
|
||||
function sign_key(exported_session_key, is_client) {
|
||||
if (is_client) {
|
||||
return md5(Buffer.concat([exported_session_key, Buffer.from("session key to client-to-server signing key magic constant\0")]));
|
||||
} else {
|
||||
return md5(Buffer.concat([exported_session_key, Buffer.from("session key to server-to-client signing key magic constant\0")]));
|
||||
}
|
||||
}
|
||||
|
||||
/// NTLMv2 security interface generate a seal key
|
||||
/// By using MD5 of the session key + a static member (sentense)
|
||||
function seal_key(exported_session_key, is_client) {
|
||||
if (is_client) {
|
||||
return md5(Buffer.concat([exported_session_key, Buffer.from("session key to client-to-server sealing key magic constant\0")]));
|
||||
} else {
|
||||
return md5(Buffer.concat([exported_session_key, Buffer.from("session key to server-to-client sealing key magic constant\0")]));
|
||||
}
|
||||
}
|
||||
|
||||
/// We are now able to build a security interface
|
||||
/// that will be used by the CSSP manager to cipherring message (private keys)
|
||||
/// To detect MITM attack
|
||||
function build_security_interface(ntlm) {
|
||||
const obj = {};
|
||||
if (ntlm) {
|
||||
obj.signing_key = sign_key(ntlm.exported_session_key, true);
|
||||
obj.verify_key = sign_key(ntlm.exported_session_key, false);
|
||||
const client_sealing_key = seal_key(ntlm.exported_session_key, true);
|
||||
const server_sealing_key = seal_key(ntlm.exported_session_key, false);
|
||||
obj.encrypt = crypto.createCipheriv('rc4', client_sealing_key, null);
|
||||
obj.decrypt = crypto.createCipheriv('rc4', server_sealing_key, null);
|
||||
}
|
||||
obj.seq_num = 0;
|
||||
|
||||
obj.gss_wrapex = function (data) {
|
||||
const encrypted_data = obj.encrypt.update(data);
|
||||
const signature = mac(obj.encrypt, obj.signing_key, obj.seq_num, data);
|
||||
obj.seq_num++;
|
||||
return Buffer.concat([signature, encrypted_data]);
|
||||
}
|
||||
|
||||
obj.gss_unwrapex = function (data) {
|
||||
const version = data.readInt32LE(0);
|
||||
const checksum = data.slice(4, 12);
|
||||
const seqnum = data.readInt32LE(12);
|
||||
const payload = data.slice(16);
|
||||
const plaintext_payload = obj.decrypt.update(payload);
|
||||
const plaintext_checksum = obj.decrypt.update(checksum);
|
||||
const seqnumbuf = Buffer.alloc(4);
|
||||
seqnumbuf.writeInt32LE(seqnum, 0);
|
||||
const computed_checksum = hmac_md5(obj.verify_key, Buffer.concat([seqnumbuf, plaintext_payload])).slice(0, 8);
|
||||
if (!plaintext_checksum.equals(computed_checksum)) { console.log("Invalid checksum on NTLMv2"); }
|
||||
return plaintext_payload;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function Create_Ntlm() {
|
||||
return {
|
||||
/// Microsoft Domain for Active Directory
|
||||
domain: "", //String,
|
||||
/// Username
|
||||
user: "", //String,
|
||||
/// Password
|
||||
password: "", // String,
|
||||
/// Key generated from NTLM hash
|
||||
response_key_nt: null, // Buffer
|
||||
/// Key generated from NTLM hash
|
||||
response_key_lm: null, // Buffer
|
||||
/// Keep trace of each messages to compute a final hash
|
||||
negotiate_message: null, // Buffer
|
||||
/// Key use to ciphering messages
|
||||
exported_session_key: crypto.randomBytes(16), // Buffer
|
||||
/// True if session use unicode
|
||||
is_unicode: false // Boolean
|
||||
}
|
||||
}
|
||||
|
||||
function authenticate_message(lm_challenge_response, nt_challenge_response, domain, user, workstation, encrypted_random_session_key, flags) {
|
||||
const payload = Buffer.concat([lm_challenge_response, nt_challenge_response, domain, user, workstation, encrypted_random_session_key]);
|
||||
const offset = ((flags & NegotiateFlags.NtlmsspNegociateVersion) == 0) ? 80 : 88;
|
||||
const buf = Buffer.alloc(offset - 16);
|
||||
buf.write('4e544c4d53535000', 0, 8, 'hex'); // Signature
|
||||
buf.writeInt32LE(3, 8); // MessageType
|
||||
buf.writeInt16LE(lm_challenge_response.length, 12); // LmChallengeResponseLen
|
||||
buf.writeInt16LE(lm_challenge_response.length, 14); // LmChallengeResponseMaxLen
|
||||
buf.writeInt32LE(offset, 16); // LmChallengeResponseBufferOffset
|
||||
buf.writeInt16LE(nt_challenge_response.length, 20); // NtChallengeResponseLen
|
||||
buf.writeInt16LE(nt_challenge_response.length, 22); // NtChallengeResponseMaxLen
|
||||
buf.writeInt32LE(offset + lm_challenge_response.length, 24); // NtChallengeResponseBufferOffset
|
||||
buf.writeInt16LE(domain.length, 28); // DomainNameLen
|
||||
buf.writeInt16LE(domain.length, 30); // DomainNameMaxLen
|
||||
buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length, 32); // DomainNameBufferOffset
|
||||
buf.writeInt16LE(user.length, 36); // UserNameLen
|
||||
buf.writeInt16LE(user.length, 38); // UserNameMaxLen
|
||||
buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length, 40); // UserNameBufferOffset
|
||||
buf.writeInt16LE(workstation.length, 44); // WorkstationLen
|
||||
buf.writeInt16LE(workstation.length, 46); // WorkstationMaxLen
|
||||
buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length, 48); // WorkstationBufferOffset
|
||||
buf.writeInt16LE(encrypted_random_session_key.length, 52); // EncryptedRandomSessionLen
|
||||
buf.writeInt16LE(encrypted_random_session_key.length, 54); // EncryptedRandomSessionMaxLen
|
||||
buf.writeInt32LE(offset + lm_challenge_response.length + nt_challenge_response.length + domain.length + user.length + workstation.length, 56); // EncryptedRandomSessionBufferOffset
|
||||
buf.writeInt32LE(flags, 60); // NegotiateFlags
|
||||
if ((flags & NegotiateFlags.NtlmsspNegociateVersion) != 0) {
|
||||
buf.writeUInt8(MajorVersion.WindowsMajorVersion6, 64); // ProductMajorVersion
|
||||
buf.writeUInt8(MinorVersion.WindowsMinorVersion0, 65); // ProductMinorVersion
|
||||
buf.writeInt16LE(6002, 66); // ProductBuild
|
||||
//buf.writeInt16LE(0, 68); // Reserved
|
||||
//buf.writeUInt8(0, 70); // Reserved
|
||||
buf.writeUInt8(NTLMRevision.NtlmSspRevisionW2K3, 71); // NTLMRevisionCurrent
|
||||
}
|
||||
return [buf, payload];
|
||||
}
|
||||
|
||||
function create_ts_authinfo(auth_info) {
|
||||
asn1obj =
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, auth_info.toString('binary'))
|
||||
])
|
||||
]);
|
||||
return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
|
||||
}
|
||||
|
||||
function create_ts_credentials(domain, user, password) {
|
||||
var asn1obj =
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, domain.toString('binary'))
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, user.toString('binary'))
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, password.toString('binary'))
|
||||
])
|
||||
]);
|
||||
const ts_password_cred_encoded = asn1.toDer(asn1obj).data;
|
||||
asn1obj =
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(1)),
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, ts_password_cred_encoded)
|
||||
])
|
||||
]);
|
||||
return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
|
||||
}
|
||||
|
||||
function create_ts_authenticate(nego, pub_key_auth) {
|
||||
// Create create_ts_request
|
||||
const asn1obj =
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(2)),
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, nego.toString('binary'))
|
||||
])
|
||||
])
|
||||
])
|
||||
]),
|
||||
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 3, true, [
|
||||
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, pub_key_auth.toString('binary'))
|
||||
]),
|
||||
]);
|
||||
|
||||
// Serialize an ASN.1 object to DER format
|
||||
return Buffer.from(asn1.toDer(asn1obj).data, 'binary');
|
||||
}
|
||||
|
||||
function read_challenge_message(ntlm, derBuffer) {
|
||||
|
||||
//console.log('ntlm.negotiate_message', ntlm.negotiate_message.toString('hex'));
|
||||
//ntlm.negotiate_message = Buffer.from('4e544c4d53535000010000003582086000000000000000000000000000000000', 'hex');
|
||||
|
||||
// ********
|
||||
//ntlm.exported_session_key = Buffer.from('9a1ed052e932834a311daf90c2750219', 'hex'); // *************************
|
||||
//derBuffer = Buffer.from('4e544c4d53535000020000000e000e003800000035828a6259312ef59a4517dd000000000000000058005800460000000a00614a0000000f430045004e005400520041004c0002000e00430045004e005400520041004c0001000e00430045004e005400520041004c0004000e00430065006e007400720061006c0003000e00430065006e007400720061006c00070008007b7b3bee9e5ad80100000000', 'hex');
|
||||
|
||||
//console.log("YST: read_challenge_message1: ", derBuffer.toString('hex'));
|
||||
|
||||
const headerSignature = derBuffer.slice(0, 8);
|
||||
if (headerSignature.toString('hex') != '4e544c4d53535000') { console.log('BAD SIGNATURE'); }
|
||||
const messageType = derBuffer.readInt32LE(8);
|
||||
if (messageType != 2) { console.log('BAD MESSAGE TYPE'); }
|
||||
const targetNameLen = derBuffer.readInt16LE(12);
|
||||
const targetNameLenMax = derBuffer.readInt16LE(14);
|
||||
const targetNameBufferOffset = derBuffer.readInt32LE(16);
|
||||
const negotiateFlags = derBuffer.readInt32LE(20);
|
||||
const serverChallenge = derBuffer.slice(24, 32);
|
||||
const reserved = derBuffer.slice(32, 40);
|
||||
if (reserved.toString('hex') != '0000000000000000') { console.log('BAD RESERVED'); }
|
||||
const targetInfoLen = derBuffer.readInt16LE(40);
|
||||
const targetInfoLenMax = derBuffer.readInt16LE(42);
|
||||
const targetInfoBufferOffset = derBuffer.readInt32LE(44);
|
||||
const targetName = derBuffer.slice(targetNameBufferOffset, targetNameBufferOffset + targetNameLen);
|
||||
const targetInfoBuf = derBuffer.slice(targetInfoBufferOffset, targetInfoBufferOffset + targetInfoLen);
|
||||
const targetInfo = decodeTargetInfo(derBuffer.slice(targetInfoBufferOffset, targetInfoBufferOffset + targetInfoLen));
|
||||
const timestamp = targetInfo[7];
|
||||
//const timestamp = Buffer.from('7b7b3bee9e5ad801', 'hex'); // **************
|
||||
if (timestamp == null) { console.log('NO TIMESTAMP'); }
|
||||
const clientChallenge = crypto.randomBytes(8);
|
||||
//const clientChallenge = Buffer.from('10aac9679ef64e66', 'hex'); // *****************************
|
||||
const response_key_nt = ntowfv2(ntlm.password, ntlm.user, ntlm.domain); // Password, Username, Domain
|
||||
const response_key_lm = lmowfv2(ntlm.password, ntlm.user, ntlm.domain); // Password, Username, Domain
|
||||
|
||||
//console.log("YST: target_name:", targetInfoBuf.toString('hex'));
|
||||
//console.log("YST: timestamp:", timestamp.toString('hex'));
|
||||
//console.log('YST: client_challenge:', clientChallenge.toString('hex'));
|
||||
//console.log("YST: response_key_nt:", response_key_nt.toString('hex'));
|
||||
//console.log("YST: response_key_lm:", response_key_lm.toString('hex'));
|
||||
|
||||
var resp = compute_response_v2(response_key_nt, response_key_lm, serverChallenge, clientChallenge, timestamp, targetInfoBuf);
|
||||
const nt_challenge_response = resp[0];
|
||||
const lm_challenge_response = resp[1];
|
||||
const session_base_key = resp[2];
|
||||
|
||||
//console.log('YST: nt_challenge_response:', nt_challenge_response.toString('hex'));
|
||||
//console.log('YST: lm_challenge_response:', lm_challenge_response.toString('hex'));
|
||||
//console.log("YST: session_base_key:", session_base_key.toString('hex'));
|
||||
|
||||
const key_exchange_key = kx_key_v2(session_base_key, lm_challenge_response, serverChallenge);
|
||||
const encrypted_random_session_key = rc4k(key_exchange_key, ntlm.exported_session_key);
|
||||
|
||||
//console.log("YST: key_exchange_key:", key_exchange_key.toString('hex'));
|
||||
//console.log("YST: self.exported_session_key:", ntlm.exported_session_key.toString('hex'));
|
||||
//console.log("YST: encrypted_random_session_key:", encrypted_random_session_key.toString('hex'));
|
||||
|
||||
ntlm.is_unicode = ((negotiateFlags & 1) != 0)
|
||||
//console.log("YST: self.is_unicode: {}", ntlm.is_unicode);
|
||||
var xdomain = null;
|
||||
var xuser = null;
|
||||
if (ntlm.is_unicode) {
|
||||
xdomain = toUnicode(ntlm.domain);
|
||||
xuser = toUnicode(ntlm.user);
|
||||
} else {
|
||||
xdomain = Buffer.from(ntlm.domain, 'utf8');
|
||||
xuser = Buffer.from(ntlm.user, 'utf8');
|
||||
}
|
||||
|
||||
//console.log("YST: domain:", xdomain.toString('hex'));
|
||||
//console.log("YST: user:", xuser.toString('hex'));
|
||||
|
||||
const auth_message_compute = authenticate_message(lm_challenge_response, nt_challenge_response, xdomain, xuser, zeroBuffer(0), encrypted_random_session_key, negotiateFlags);
|
||||
|
||||
// Write a tmp message to compute MIC and then include it into final message
|
||||
const tmp_final_auth_message = Buffer.concat([auth_message_compute[0], zeroBuffer(16), auth_message_compute[1]]);
|
||||
|
||||
//console.log("YST: tmp_final_auth_message: {}", tmp_final_auth_message.toString('hex'));
|
||||
|
||||
const signature = mic(ntlm.exported_session_key, ntlm.negotiate_message, derBuffer, tmp_final_auth_message);
|
||||
|
||||
//console.log("YST: signature: {}", signature.toString('hex'));
|
||||
|
||||
const r = Buffer.concat([auth_message_compute[0], signature, auth_message_compute[1]]);
|
||||
|
||||
//console.log("YST: read_challenge_message2: {}", r.toString('hex'));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
function unitTest() {
|
||||
console.log('--- Starting RDP NLA Unit Tests');
|
||||
|
||||
// Test format of the first client message
|
||||
var r = create_negotiate_message();
|
||||
console.log(compareArray(bufToArr(r), [78, 84, 76, 77, 83, 83, 80, 0, 1, 0, 0, 0, 53, 130, 8, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) ? "negotiate_message passed." : "negotiate_message failed.");
|
||||
|
||||
// Test of MD4 hash function
|
||||
r = md4(Buffer.from("foo"));
|
||||
console.log(compareArray(bufToArr(r), [0x0a, 0xc6, 0x70, 0x0c, 0x49, 0x1d, 0x70, 0xfb, 0x86, 0x50, 0x94, 0x0b, 0x1c, 0xa1, 0xe4, 0xb2]) ? "RC4 passed." : "RC4 failed.");
|
||||
|
||||
// Test of the unicode function
|
||||
r = toUnicode("foo");
|
||||
console.log(compareArray(bufToArr(r), [0x66, 0x00, 0x6f, 0x00, 0x6f, 0x00]) ? "Unicode passed." : "Unicode failed.");
|
||||
|
||||
// Test HMAC_MD5 function
|
||||
r = hmac_md5(Buffer.from("foo"), Buffer.from("bar"));
|
||||
console.log(compareArray(bufToArr(r), [0x0c, 0x7a, 0x25, 0x02, 0x81, 0x31, 0x5a, 0xb8, 0x63, 0x54, 0x9f, 0x66, 0xcd, 0x8a, 0x3a, 0x53]) ? "HMAC_MD5 passed." : "HMAC_MD5 failed.");
|
||||
|
||||
// Test NTOWFv2 function
|
||||
r = ntowfv2("foo", "user", "domain");
|
||||
console.log(compareArray(bufToArr(r), [0x6e, 0x53, 0xb9, 0x0, 0x97, 0x8c, 0x87, 0x1f, 0x91, 0xde, 0x6, 0x44, 0x9d, 0x8b, 0x8b, 0x81]) ? "NTOWFv2 passed." : "NTOWFv2 failed.");
|
||||
|
||||
// Test LMOWFv2 function
|
||||
r = ntowfv2("foo", "user", "domain");
|
||||
console.log(compareArray(bufToArr(r), ntowfv2("foo", "user", "domain")) ? "LMOWFv2 passed." : "LMOWFv2 failed.");
|
||||
|
||||
// Test compute response v2 function
|
||||
r = compute_response_v2(Buffer.from("a"), Buffer.from("b"), Buffer.from("c"), Buffer.from("d"), Buffer.from("e"), Buffer.from("f"));
|
||||
console.log(compareArray(bufToArr(r[0]), [0xb4, 0x23, 0x84, 0xf, 0x6e, 0x83, 0xc1, 0x5a, 0x45, 0x4f, 0x4c, 0x92, 0x7a, 0xf2, 0xc3, 0x3e, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x64, 0x0, 0x0, 0x0, 0x0, 0x66]) ? "responsev2 1 passed." : "responsev2 1 failed.");
|
||||
console.log(compareArray(bufToArr(r[1]), [0x56, 0xba, 0xff, 0x2d, 0x98, 0xbe, 0xcd, 0xa5, 0x6d, 0xe6, 0x17, 0x89, 0xe1, 0xed, 0xca, 0xae, 0x64]) ? "responsev2 2 passed." : "responsev2 2 failed.");
|
||||
console.log(compareArray(bufToArr(r[2]), [0x40, 0x3b, 0x33, 0xe5, 0x24, 0x34, 0x3c, 0xc3, 0x24, 0xa0, 0x4d, 0x77, 0x75, 0x34, 0xa4, 0xd0]) ? "responsev2 3 passed." : "responsev2 3 failed.");
|
||||
|
||||
// Test of rc4k function
|
||||
r = rc4k(Buffer.from("foo"), Buffer.from("bar"));
|
||||
console.log(compareArray(bufToArr(r), [201, 67, 159]) ? "rc4k passed." : "rc4k failed.");
|
||||
|
||||
// Test of sign_key function
|
||||
r = sign_key(Buffer.from("foo"), true);
|
||||
console.log(compareArray(bufToArr(r), [253, 238, 149, 155, 221, 78, 43, 179, 82, 61, 111, 132, 168, 68, 222, 15]) ? "sign_key 1 passed." : "sign_key 1 failed.");
|
||||
r = sign_key(Buffer.from("foo"), false);
|
||||
console.log(compareArray(bufToArr(r), [90, 201, 12, 225, 140, 156, 151, 61, 156, 56, 31, 254, 10, 223, 252, 74]) ? "sign_key 2 passed." : "sign_key 2 failed.");
|
||||
|
||||
// Test of seal_key function
|
||||
r = seal_key(Buffer.from("foo"), true);
|
||||
console.log(compareArray(bufToArr(r), [20, 213, 185, 176, 168, 142, 134, 244, 36, 249, 89, 247, 180, 36, 162, 101]) ? "seal_key 1 passed." : "seal_key 1 failed.");
|
||||
r = seal_key(Buffer.from("foo"), false);
|
||||
console.log(compareArray(bufToArr(r), [64, 125, 160, 17, 144, 165, 62, 226, 22, 125, 128, 31, 103, 141, 55, 40]) ? "seal_key 2 passed." : "seal_key 2 failed.");
|
||||
|
||||
// Test signature function
|
||||
var rc4 = crypto.createCipheriv('rc4', Buffer.from("foo"), null);
|
||||
r = mac(rc4, Buffer.from("bar"), 0, Buffer.from("data"));
|
||||
console.log(compareArray(bufToArr(r), [1, 0, 0, 0, 77, 211, 144, 84, 51, 242, 202, 176, 0, 0, 0, 0]) ? "Signature passed." : "Signature failed.");
|
||||
|
||||
// Test challenge message
|
||||
r = authenticate_message(Buffer.from("foo"), Buffer.from("foo"), Buffer.from("domain"), Buffer.from("user"), Buffer.from("workstation"), Buffer.from("foo"), 0);
|
||||
var buf = Buffer.concat([r[0], Buffer.alloc(16), r[1]]);
|
||||
console.log(compareArray(bufToArr(buf), [78, 84, 76, 77, 83, 83, 80, 0, 3, 0, 0, 0, 3, 0, 3, 0, 80, 0, 0, 0, 3, 0, 3, 0, 83, 0, 0, 0, 6, 0, 6, 0, 86, 0, 0, 0, 4, 0, 4, 0, 92, 0, 0, 0, 11, 0, 11, 0, 96, 0, 0, 0, 3, 0, 3, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 111, 111, 102, 111, 111, 100, 111, 109, 97, 105, 110, 117, 115, 101, 114, 119, 111, 114, 107, 115, 116, 97, 116, 105, 111, 110, 102, 111, 111]) ? "Challenge message passed." : "Challenge message failed.");
|
||||
|
||||
// Test RC4
|
||||
rc4 = crypto.createCipheriv('rc4', Buffer.from("foo"), null);
|
||||
r = rc4.update(Buffer.from("bar"));
|
||||
console.log(compareArray(bufToArr(r), [201, 67, 159]) ? "RC4 1 passed." : "RC4 1 failed.");
|
||||
r = rc4.update(Buffer.from("bar"));
|
||||
console.log(compareArray(bufToArr(r), [75, 169, 19]) ? "RC4 2 passed." : "RC4 2 failed.");
|
||||
|
||||
// Test create_ts_authenticate
|
||||
r = create_ts_authenticate(Buffer.from("000102", 'hex'), Buffer.from("000102", 'hex'));
|
||||
console.log(compareArray(bufToArr(r), [48, 25, 160, 3, 2, 1, 2, 161, 11, 48, 9, 48, 7, 160, 5, 4, 3, 0, 1, 2, 163, 5, 4, 3, 0, 1, 2]) ? "create_ts_authenticate passed." : "create_ts_authenticate failed.");
|
||||
|
||||
// Test test_create_ts_credentials
|
||||
r = create_ts_credentials(Buffer.from("domain"), Buffer.from("user"), Buffer.from("password"));
|
||||
console.log(compareArray(bufToArr(r), [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]) ? "test_create_ts_credentials passed." : "test_create_ts_credentials failed.");
|
||||
|
||||
// Test create_ts_authinfo
|
||||
r = create_ts_authinfo(Buffer.from("foo"));
|
||||
console.log(compareArray(bufToArr(r), [48, 12, 160, 3, 2, 1, 2, 162, 5, 4, 3, 102, 111, 111]) ? "create_ts_authinfo passed." : "create_ts_authinfo failed.");
|
||||
|
||||
console.log('--- RDP NLA Unit Tests Completed');
|
||||
}
|
||||
704
rdp/protocol/pdu/caps.js
Normal file
704
rdp/protocol/pdu/caps.js
Normal file
|
|
@ -0,0 +1,704 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
var error = require('../../core').error;
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240486.aspx
|
||||
*/
|
||||
var CapsType = {
|
||||
CAPSTYPE_GENERAL : 0x0001,
|
||||
CAPSTYPE_BITMAP : 0x0002,
|
||||
CAPSTYPE_ORDER : 0x0003,
|
||||
CAPSTYPE_BITMAPCACHE : 0x0004,
|
||||
CAPSTYPE_CONTROL : 0x0005,
|
||||
CAPSTYPE_ACTIVATION : 0x0007,
|
||||
CAPSTYPE_POINTER : 0x0008,
|
||||
CAPSTYPE_SHARE : 0x0009,
|
||||
CAPSTYPE_COLORCACHE : 0x000A,
|
||||
CAPSTYPE_SOUND : 0x000C,
|
||||
CAPSTYPE_INPUT : 0x000D,
|
||||
CAPSTYPE_FONT : 0x000E,
|
||||
CAPSTYPE_BRUSH : 0x000F,
|
||||
CAPSTYPE_GLYPHCACHE : 0x0010,
|
||||
CAPSTYPE_OFFSCREENCACHE : 0x0011,
|
||||
CAPSTYPE_BITMAPCACHE_HOSTSUPPORT : 0x0012,
|
||||
CAPSTYPE_BITMAPCACHE_REV2 : 0x0013,
|
||||
CAPSTYPE_VIRTUALCHANNEL : 0x0014,
|
||||
CAPSTYPE_DRAWNINEGRIDCACHE : 0x0015,
|
||||
CAPSTYPE_DRAWGDIPLUS : 0x0016,
|
||||
CAPSTYPE_RAIL : 0x0017,
|
||||
CAPSTYPE_WINDOW : 0x0018,
|
||||
CAPSETTYPE_COMPDESK : 0x0019,
|
||||
CAPSETTYPE_MULTIFRAGMENTUPDATE : 0x001A,
|
||||
CAPSETTYPE_LARGE_POINTER : 0x001B,
|
||||
CAPSETTYPE_SURFACE_COMMANDS : 0x001C,
|
||||
CAPSETTYPE_BITMAP_CODECS : 0x001D,
|
||||
CAPSSETTYPE_FRAME_ACKNOWLEDGE : 0x001E
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
*/
|
||||
var MajorType = {
|
||||
OSMAJORTYPE_UNSPECIFIED : 0x0000,
|
||||
OSMAJORTYPE_WINDOWS : 0x0001,
|
||||
OSMAJORTYPE_OS2 : 0x0002,
|
||||
OSMAJORTYPE_MACINTOSH : 0x0003,
|
||||
OSMAJORTYPE_UNIX : 0x0004,
|
||||
OSMAJORTYPE_IOS : 0x0005,
|
||||
OSMAJORTYPE_OSX : 0x0006,
|
||||
OSMAJORTYPE_ANDROID : 0x0007
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
*/
|
||||
var MinorType = {
|
||||
OSMINORTYPE_UNSPECIFIED : 0x0000,
|
||||
OSMINORTYPE_WINDOWS_31X : 0x0001,
|
||||
OSMINORTYPE_WINDOWS_95 : 0x0002,
|
||||
OSMINORTYPE_WINDOWS_NT : 0x0003,
|
||||
OSMINORTYPE_OS2_V21 : 0x0004,
|
||||
OSMINORTYPE_POWER_PC : 0x0005,
|
||||
OSMINORTYPE_MACINTOSH : 0x0006,
|
||||
OSMINORTYPE_NATIVE_XSERVER : 0x0007,
|
||||
OSMINORTYPE_PSEUDO_XSERVER : 0x0008,
|
||||
OSMINORTYPE_WINDOWS_RT : 0x0009
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
*/
|
||||
var GeneralExtraFlag = {
|
||||
FASTPATH_OUTPUT_SUPPORTED : 0x0001,
|
||||
NO_BITMAP_COMPRESSION_HDR : 0x0400,
|
||||
LONG_CREDENTIALS_SUPPORTED : 0x0004,
|
||||
AUTORECONNECT_SUPPORTED : 0x0008,
|
||||
ENC_SALTED_CHECKSUM : 0x0010
|
||||
};
|
||||
|
||||
var Boolean = {
|
||||
FALSE : 0x00,
|
||||
TRUE : 0x01
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240556.aspx
|
||||
*/
|
||||
var OrderFlag = {
|
||||
NEGOTIATEORDERSUPPORT : 0x0002,
|
||||
ZEROBOUNDSDELTASSUPPORT : 0x0008,
|
||||
COLORINDEXSUPPORT : 0x0020,
|
||||
SOLIDPATTERNBRUSHONLY : 0x0040,
|
||||
ORDERFLAGS_EXTRA_FLAGS : 0x0080
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240556.aspx
|
||||
*/
|
||||
var Order = {
|
||||
TS_NEG_DSTBLT_INDEX : 0x00,
|
||||
TS_NEG_PATBLT_INDEX : 0x01,
|
||||
TS_NEG_SCRBLT_INDEX : 0x02,
|
||||
TS_NEG_MEMBLT_INDEX : 0x03,
|
||||
TS_NEG_MEM3BLT_INDEX : 0x04,
|
||||
TS_NEG_DRAWNINEGRID_INDEX : 0x07,
|
||||
TS_NEG_LINETO_INDEX : 0x08,
|
||||
TS_NEG_MULTI_DRAWNINEGRID_INDEX : 0x09,
|
||||
TS_NEG_SAVEBITMAP_INDEX : 0x0B,
|
||||
TS_NEG_MULTIDSTBLT_INDEX : 0x0F,
|
||||
TS_NEG_MULTIPATBLT_INDEX : 0x10,
|
||||
TS_NEG_MULTISCRBLT_INDEX : 0x11,
|
||||
TS_NEG_MULTIOPAQUERECT_INDEX : 0x12,
|
||||
TS_NEG_FAST_INDEX_INDEX : 0x13,
|
||||
TS_NEG_POLYGON_SC_INDEX : 0x14,
|
||||
TS_NEG_POLYGON_CB_INDEX : 0x15,
|
||||
TS_NEG_POLYLINE_INDEX : 0x16,
|
||||
TS_NEG_FAST_GLYPH_INDEX : 0x18,
|
||||
TS_NEG_ELLIPSE_SC_INDEX : 0x19,
|
||||
TS_NEG_ELLIPSE_CB_INDEX : 0x1A,
|
||||
TS_NEG_INDEX_INDEX : 0x1B
|
||||
};
|
||||
|
||||
var OrderEx = {
|
||||
ORDERFLAGS_EX_CACHE_BITMAP_REV3_SUPPORT : 0x0002,
|
||||
ORDERFLAGS_EX_ALTSEC_FRAME_MARKER_SUPPORT : 0x0004
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240563.aspx
|
||||
*/
|
||||
var InputFlags = {
|
||||
INPUT_FLAG_SCANCODES : 0x0001,
|
||||
INPUT_FLAG_MOUSEX : 0x0004,
|
||||
INPUT_FLAG_FASTPATH_INPUT : 0x0008,
|
||||
INPUT_FLAG_UNICODE : 0x0010,
|
||||
INPUT_FLAG_FASTPATH_INPUT2 : 0x0020,
|
||||
INPUT_FLAG_UNUSED1 : 0x0040,
|
||||
INPUT_FLAG_UNUSED2 : 0x0080,
|
||||
TS_INPUT_FLAG_MOUSE_HWHEEL : 0x0100
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240564.aspx
|
||||
*/
|
||||
var BrushSupport = {
|
||||
BRUSH_DEFAULT : 0x00000000,
|
||||
BRUSH_COLOR_8x8 : 0x00000001,
|
||||
BRUSH_COLOR_FULL : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240565.aspx
|
||||
*/
|
||||
var GlyphSupport = {
|
||||
GLYPH_SUPPORT_NONE : 0x0000,
|
||||
GLYPH_SUPPORT_PARTIAL : 0x0001,
|
||||
GLYPH_SUPPORT_FULL : 0x0002,
|
||||
GLYPH_SUPPORT_ENCODE : 0x0003
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240550.aspx
|
||||
*/
|
||||
var OffscreenSupportLevel = {
|
||||
FALSE : 0x00000000,
|
||||
TRUE : 0x00000001
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240551.aspx
|
||||
*/
|
||||
var VirtualChannelCompressionFlag = {
|
||||
VCCAPS_NO_COMPR : 0x00000000,
|
||||
VCCAPS_COMPR_SC : 0x00000001,
|
||||
VCCAPS_COMPR_CS_8K : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240552.aspx
|
||||
*/
|
||||
var SoundFlag = {
|
||||
NONE : 0x0000,
|
||||
SOUND_BEEPS_FLAG : 0x0001
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240549.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function generalCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_GENERAL,
|
||||
osMajorType : new type.UInt16Le(),
|
||||
osMinorType : new type.UInt16Le(),
|
||||
protocolVersion : new type.UInt16Le(0x0200, {constant : true}),
|
||||
pad2octetsA : new type.UInt16Le(),
|
||||
generalCompressionTypes : new type.UInt16Le(0, {constant : true}),
|
||||
extraFlags : new type.UInt16Le(),
|
||||
updateCapabilityFlag : new type.UInt16Le(0, {constant : true}),
|
||||
remoteUnshareFlag : new type.UInt16Le(0, {constant : true}),
|
||||
generalCompressionLevel : new type.UInt16Le(0, {constant : true}),
|
||||
refreshRectSupport : new type.UInt8(),
|
||||
suppressOutputSupport : new type.UInt8()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240554.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function bitmapCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_BITMAP,
|
||||
preferredBitsPerPixel : new type.UInt16Le(),
|
||||
receive1BitPerPixel : new type.UInt16Le(0x0001),
|
||||
receive4BitsPerPixel : new type.UInt16Le(0x0001),
|
||||
receive8BitsPerPixel : new type.UInt16Le(0x0001),
|
||||
desktopWidth : new type.UInt16Le(),
|
||||
desktopHeight : new type.UInt16Le(),
|
||||
pad2octets : new type.UInt16Le(),
|
||||
desktopResizeFlag : new type.UInt16Le(),
|
||||
bitmapCompressionFlag : new type.UInt16Le(0x0001, {constant : true}),
|
||||
highColorFlags : new type.UInt8(0),
|
||||
drawingFlags : new type.UInt8(),
|
||||
multipleRectangleSupport : new type.UInt16Le(0x0001, {constant : true}),
|
||||
pad2octetsB : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240556.aspx
|
||||
* @param orders {type.BinaryString|null} list of available orders
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function orderCapability(orders, opt) {
|
||||
if(orders && orders.size() !== 32) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_CAPS_BAD_ORDERS_SIZE');
|
||||
}
|
||||
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_ORDER,
|
||||
terminalDescriptor : new type.BinaryString(Buffer.from(Array(16 + 1).join('\x00'), 'binary'), {readLength : new type.CallableValue(16)}),
|
||||
pad4octetsA : new type.UInt32Le(0),
|
||||
desktopSaveXGranularity : new type.UInt16Le(1),
|
||||
desktopSaveYGranularity : new type.UInt16Le(20),
|
||||
pad2octetsA : new type.UInt16Le(0),
|
||||
maximumOrderLevel : new type.UInt16Le(1),
|
||||
numberFonts : new type.UInt16Le(),
|
||||
orderFlags : new type.UInt16Le(OrderFlag.NEGOTIATEORDERSUPPORT),
|
||||
orderSupport : orders || new type.Factory(function(s) {
|
||||
self.orderSupport = new type.BinaryString(null, {readLength : new type.CallableValue(32)}).read(s);
|
||||
}),
|
||||
textFlags : new type.UInt16Le(),
|
||||
orderSupportExFlags : new type.UInt16Le(),
|
||||
pad4octetsB : new type.UInt32Le(),
|
||||
desktopSaveSize : new type.UInt32Le(480 * 480),
|
||||
pad2octetsC : new type.UInt16Le(),
|
||||
pad2octetsD : new type.UInt16Le(),
|
||||
textANSICodePage : new type.UInt16Le(0),
|
||||
pad2octetsE : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240559.aspx
|
||||
* @param opt type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function bitmapCacheCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_BITMAPCACHE,
|
||||
pad1 : new type.UInt32Le(),
|
||||
pad2 : new type.UInt32Le(),
|
||||
pad3 : new type.UInt32Le(),
|
||||
pad4 : new type.UInt32Le(),
|
||||
pad5 : new type.UInt32Le(),
|
||||
pad6 : new type.UInt32Le(),
|
||||
cache0Entries : new type.UInt16Le(),
|
||||
cache0MaximumCellSize : new type.UInt16Le(),
|
||||
cache1Entries : new type.UInt16Le(),
|
||||
cache1MaximumCellSize : new type.UInt16Le(),
|
||||
cache2Entries : new type.UInt16Le(),
|
||||
cache2MaximumCellSize : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param isServer {boolean} true if in server mode
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function pointerCapability(isServer, opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_POINTER,
|
||||
colorPointerFlag : new type.UInt16Le(),
|
||||
colorPointerCacheSize : new type.UInt16Le(20),
|
||||
//old version of rdp doesn't support ...
|
||||
pointerCacheSize : new type.UInt16Le(null, {conditional : function() {
|
||||
return isServer || false;
|
||||
}})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240563.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function inputCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_INPUT,
|
||||
inputFlags : new type.UInt16Le(),
|
||||
pad2octetsA : new type.UInt16Le(),
|
||||
// same value as gcc.ClientCoreSettings.kbdLayout
|
||||
keyboardLayout : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettings.keyboardType
|
||||
keyboardType : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettings.keyboardSubType
|
||||
keyboardSubType : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettings.keyboardFnKeys
|
||||
keyboardFunctionKey : new type.UInt32Le(),
|
||||
// same value as gcc.ClientCoreSettingrrs.imeFileName
|
||||
imeFileName : new type.BinaryString(Buffer.from(Array(64 + 1).join('\x00'), 'binary'), {readLength : new type.CallableValue(64)})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240564.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function brushCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_BRUSH,
|
||||
brushSupportLevel : new type.UInt32Le(BrushSupport.BRUSH_DEFAULT)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240566.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function cacheEntry(opt) {
|
||||
var self = {
|
||||
cacheEntries : new type.UInt16Le(),
|
||||
cacheMaximumCellSize : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240565.aspx
|
||||
* @param entries {type.Component} cache entries
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function glyphCapability(entries, opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_GLYPHCACHE,
|
||||
glyphCache : entries || new type.Factory(function(s) {
|
||||
self.glyphCache = new type.Component([]);
|
||||
for(var i = 0; i < 10; i++) {
|
||||
self.glyphCache.obj.push(cacheEntry().read(s));
|
||||
}
|
||||
}),
|
||||
fragCache : new type.UInt32Le(),
|
||||
// all fonts are sent with bitmap format (very expensive)
|
||||
glyphSupportLevel : new type.UInt16Le(GlyphSupport.GLYPH_SUPPORT_NONE),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240550.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function offscreenBitmapCacheCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_OFFSCREENCACHE,
|
||||
offscreenSupportLevel : new type.UInt32Le(OffscreenSupportLevel.FALSE),
|
||||
offscreenCacheSize : new type.UInt16Le(),
|
||||
offscreenCacheEntries : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240551.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function virtualChannelCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_VIRTUALCHANNEL,
|
||||
flags : new type.UInt32Le(VirtualChannelCompressionFlag.VCCAPS_NO_COMPR),
|
||||
VCChunkSize : new type.UInt32Le(null, {optional : true})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240552.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function soundCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_SOUND,
|
||||
soundFlags : new type.UInt16Le(SoundFlag.NONE),
|
||||
pad2octetsA : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240568.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function controlCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_CONTROL,
|
||||
controlFlags : new type.UInt16Le(),
|
||||
remoteDetachFlag : new type.UInt16Le(),
|
||||
controlInterest : new type.UInt16Le(0x0002),
|
||||
detachInterest : new type.UInt16Le(0x0002)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240569.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function windowActivationCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_ACTIVATION,
|
||||
helpKeyFlag : new type.UInt16Le(),
|
||||
helpKeyIndexFlag : new type.UInt16Le(),
|
||||
helpExtendedKeyFlag : new type.UInt16Le(),
|
||||
windowManagerKeyFlag : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240571.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function fontCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_FONT,
|
||||
fontSupportFlags : new type.UInt16Le(0x0001),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241564.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function colorCacheCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_COLORCACHE,
|
||||
colorTableCacheSize : new type.UInt16Le(0x0006),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240570.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function shareCapability(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSTYPE_SHARE,
|
||||
nodeId : new type.UInt16Le(),
|
||||
pad2octets : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240649.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function multiFragmentUpdate(opt) {
|
||||
var self = {
|
||||
__TYPE__ : CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE,
|
||||
MaxRequestSize : new type.UInt32Le(0)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Capability wrapper packet
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240486.aspx
|
||||
* @param cap {type.Component}
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function capability(cap, opt) {
|
||||
var self = {
|
||||
capabilitySetType : new type.UInt16Le(function() {
|
||||
return self.capability.obj.__TYPE__;
|
||||
}),
|
||||
lengthCapability : new type.UInt16Le(function() {
|
||||
return new type.Component(self).size();
|
||||
}),
|
||||
capability : cap || new type.Factory(function(s) {
|
||||
switch(self.capabilitySetType.value) {
|
||||
case CapsType.CAPSTYPE_GENERAL:
|
||||
self.capability = generalCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_BITMAP:
|
||||
self.capability = bitmapCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_ORDER:
|
||||
self.capability = orderCapability(null, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_BITMAPCACHE:
|
||||
self.capability = bitmapCacheCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_POINTER:
|
||||
self.capability = pointerCapability(false, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_INPUT:
|
||||
self.capability = inputCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_BRUSH:
|
||||
self.capability = brushCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_GLYPHCACHE:
|
||||
self.capability = glyphCapability(null, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_OFFSCREENCACHE:
|
||||
self.capability = offscreenBitmapCacheCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_VIRTUALCHANNEL:
|
||||
self.capability = virtualChannelCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_SOUND:
|
||||
self.capability = soundCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_CONTROL:
|
||||
self.capability = controlCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_ACTIVATION:
|
||||
self.capability = windowActivationCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_FONT:
|
||||
self.capability = fontCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_COLORCACHE:
|
||||
self.capability = colorCacheCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSTYPE_SHARE:
|
||||
self.capability = shareCapability({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE:
|
||||
self.capability = multiFragmentUpdate({readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
default:
|
||||
log.debug('unknown capability ' + self.capabilitySetType.value);
|
||||
self.capability = new type.BinaryString(null, {readLength : new type.CallableValue(function() {
|
||||
return self.lengthCapability.value - 4;
|
||||
})}).read(s);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
CapsType : CapsType,
|
||||
MajorType : MajorType,
|
||||
MinorType : MinorType,
|
||||
GeneralExtraFlag : GeneralExtraFlag,
|
||||
Boolean : Boolean,
|
||||
OrderFlag : OrderFlag,
|
||||
Order : Order,
|
||||
OrderEx : OrderEx,
|
||||
InputFlags : InputFlags,
|
||||
BrushSupport : BrushSupport,
|
||||
GlyphSupport : GlyphSupport,
|
||||
OffscreenSupportLevel : OffscreenSupportLevel,
|
||||
VirtualChannelCompressionFlag : VirtualChannelCompressionFlag,
|
||||
SoundFlag : SoundFlag,
|
||||
generalCapability : generalCapability,
|
||||
bitmapCapability : bitmapCapability,
|
||||
orderCapability : orderCapability,
|
||||
bitmapCacheCapability : bitmapCacheCapability,
|
||||
pointerCapability : pointerCapability,
|
||||
inputCapability : inputCapability,
|
||||
brushCapability : brushCapability,
|
||||
cacheEntry : cacheEntry,
|
||||
glyphCapability : glyphCapability,
|
||||
offscreenBitmapCacheCapability : offscreenBitmapCacheCapability,
|
||||
virtualChannelCapability : virtualChannelCapability,
|
||||
soundCapability : soundCapability,
|
||||
controlCapability : controlCapability,
|
||||
windowActivationCapability : windowActivationCapability,
|
||||
fontCapability : fontCapability,
|
||||
colorCacheCapability : colorCacheCapability,
|
||||
shareCapability : shareCapability,
|
||||
multiFragmentUpdate : multiFragmentUpdate,
|
||||
capability : capability
|
||||
};
|
||||
1151
rdp/protocol/pdu/data.js
Normal file
1151
rdp/protocol/pdu/data.js
Normal file
File diff suppressed because it is too large
Load diff
402
rdp/protocol/pdu/global.js
Normal file
402
rdp/protocol/pdu/global.js
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var events = require('events');
|
||||
var caps = require('./caps');
|
||||
var data = require('./data');
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
|
||||
/**
|
||||
* Global channel for all graphic updates
|
||||
* capabilities exchange and input handles
|
||||
*/
|
||||
function Global(transport, fastPathTransport) {
|
||||
this.transport = transport;
|
||||
this.fastPathTransport = fastPathTransport;
|
||||
// must be init via connect event
|
||||
this.userId = 0;
|
||||
this.serverCapabilities = [];
|
||||
this.clientCapabilities = [];
|
||||
}
|
||||
|
||||
//inherit from Layer
|
||||
inherits(Global, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Send formated PDU message
|
||||
* @param message {type.Component} PDU message
|
||||
*/
|
||||
Global.prototype.sendPDU = function(message) {
|
||||
this.transport.send(data.pdu(this.userId, message));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send formated Data PDU message
|
||||
* @param message {type.Component} PDU message
|
||||
*/
|
||||
Global.prototype.sendDataPDU = function(message) {
|
||||
this.sendPDU(data.dataPDU(message, this.shareId));
|
||||
};
|
||||
|
||||
/**
|
||||
* Client side of Global channel automata
|
||||
* @param transport
|
||||
*/
|
||||
function Client(transport, fastPathTransport) {
|
||||
Global.call(this, transport, fastPathTransport);
|
||||
var self = this;
|
||||
this.transport.once('connect', function(core, userId, channelId) {
|
||||
self.connect(core, userId, channelId);
|
||||
}).on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
if (this.fastPathTransport) {
|
||||
this.fastPathTransport.on('fastPathData', function (secFlag, s) {
|
||||
self.recvFastPath(secFlag, s);
|
||||
});
|
||||
}
|
||||
|
||||
// init client capabilities
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL] = caps.generalCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP] = caps.bitmapCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER] = caps.orderCapability(
|
||||
new type.Component([
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0),
|
||||
new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0), new type.UInt8(0)
|
||||
]));
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAPCACHE] = caps.bitmapCacheCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_POINTER] = caps.pointerCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_INPUT] = caps.inputCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_BRUSH] = caps.brushCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_GLYPHCACHE] = caps.glyphCapability(
|
||||
new type.Component([
|
||||
caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(),
|
||||
caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry(), caps.cacheEntry()
|
||||
]));
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_OFFSCREENCACHE] = caps.offscreenBitmapCacheCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_VIRTUALCHANNEL] = caps.virtualChannelCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSTYPE_SOUND] = caps.soundCapability();
|
||||
this.clientCapabilities[caps.CapsType.CAPSETTYPE_MULTIFRAGMENTUPDATE] = caps.multiFragmentUpdate();
|
||||
}
|
||||
|
||||
// inherit from Layer
|
||||
inherits(Client, Global);
|
||||
|
||||
/**
|
||||
* connect function
|
||||
* @param gccCore {type.Component(clientCoreData)}
|
||||
*/
|
||||
Client.prototype.connect = function(gccCore, userId, channelId) {
|
||||
this.gccCore = gccCore;
|
||||
this.userId = userId;
|
||||
this.channelId = channelId;
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvDemandActivePDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive capabilities from server
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvDemandActivePDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if (pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DEMANDACTIVEPDU) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvDemandActivePDU(s);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// store share id
|
||||
this.shareId = pdu.obj.pduMessage.obj.shareId.value;
|
||||
|
||||
// store server capabilities
|
||||
for(var i in pdu.obj.pduMessage.obj.capabilitySets.obj) {
|
||||
var cap = pdu.obj.pduMessage.obj.capabilitySets.obj[i].obj.capability;
|
||||
if(!cap.obj) {
|
||||
continue;
|
||||
}
|
||||
this.serverCapabilities[cap.obj.__TYPE__] = cap;
|
||||
}
|
||||
|
||||
this.transport.enableSecureCheckSum = !!(this.serverCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj.extraFlags.value & caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM);
|
||||
|
||||
this.sendConfirmActivePDU();
|
||||
this.sendClientFinalizeSynchronizePDU();
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerSynchronizePDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerSynchronizePDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_SYNCHRONIZE) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerSynchronizePDU(s);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlCooperatePDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerControlCooperatePDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_CONTROL
|
||||
|| pdu.obj.pduMessage.obj.pduData.obj.action.value !== data.Action.CTRLACTION_COOPERATE) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlCooperatePDU(s);
|
||||
});
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlGrantedPDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerControlGrantedPDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_CONTROL
|
||||
|| pdu.obj.pduMessage.obj.pduData.obj.action.value !== data.Action.CTRLACTION_GRANTED_CONTROL) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerControlGrantedPDU(s);
|
||||
});
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerFontMapPDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvServerFontMapPDU = function(s) {
|
||||
var pdu = data.pdu().read(s);
|
||||
if ( pdu.obj.shareControlHeader.obj.pduType.value !== data.PDUType.PDUTYPE_DATAPDU
|
||||
|| pdu.obj.pduMessage.obj.shareDataHeader.obj.pduType2.value !== data.PDUType2.PDUTYPE2_FONTMAP) {
|
||||
log.debug('ignore message type ' + pdu.obj.shareControlHeader.obj.pduType.value + ' during connection sequence');
|
||||
|
||||
// loop on state
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvServerFontMapPDU(s);
|
||||
});
|
||||
}
|
||||
|
||||
this.emit('connect');
|
||||
var self = this;
|
||||
this.transport.on('data', function(s) {
|
||||
self.recvPDU(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Main reveive fast path
|
||||
* @param secFlag {integer}
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvFastPath = function (secFlag, s) {
|
||||
while (s.availableLength() > 0) {
|
||||
var pdu = data.fastPathUpdatePDU().read(s);
|
||||
switch (pdu.obj.updateHeader.value & 0xf) {
|
||||
case data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
|
||||
this.emit('bitmap', pdu.obj.updateData.obj.rectangles.obj);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* global channel automata state
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvPDU = function(s) {
|
||||
while (s.availableLength() > 0) {
|
||||
var pdu = data.pdu().read(s);
|
||||
switch(pdu.obj.shareControlHeader.obj.pduType.value) {
|
||||
case data.PDUType.PDUTYPE_DEACTIVATEALLPDU:
|
||||
var self = this;
|
||||
this.transport.removeAllListeners('data');
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvDemandActivePDU(s);
|
||||
});
|
||||
break;
|
||||
case data.PDUType.PDUTYPE_DATAPDU:
|
||||
this.readDataPDU(pdu.obj.pduMessage)
|
||||
break;
|
||||
default:
|
||||
log.debug('ignore pdu type ' + pdu.obj.shareControlHeader.obj.pduType.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* main receive for data PDU packet
|
||||
* @param dataPDU {data.dataPDU}
|
||||
*/
|
||||
Client.prototype.readDataPDU = function (dataPDU) {
|
||||
switch(dataPDU.obj.shareDataHeader.obj.pduType2.value) {
|
||||
case data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
|
||||
break;
|
||||
case data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
|
||||
this.transport.close();
|
||||
break;
|
||||
case data.PDUType2.PDUTYPE2_SAVE_SESSION_INFO:
|
||||
this.emit('session');
|
||||
break;
|
||||
case data.PDUType2.PDUTYPE2_UPDATE:
|
||||
this.readUpdateDataPDU(dataPDU.obj.pduData)
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Main upadate pdu receive function
|
||||
* @param updateDataPDU
|
||||
*/
|
||||
Client.prototype.readUpdateDataPDU = function (updateDataPDU) {
|
||||
switch(updateDataPDU.obj.updateType.value) {
|
||||
case data.UpdateType.UPDATETYPE_BITMAP:
|
||||
this.emit('bitmap', updateDataPDU.obj.updateData.obj.rectangles.obj)
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* send all client capabilities
|
||||
*/
|
||||
Client.prototype.sendConfirmActivePDU = function () {
|
||||
var generalCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].obj;
|
||||
generalCapability.osMajorType.value = caps.MajorType.OSMAJORTYPE_WINDOWS;
|
||||
generalCapability.osMinorType.value = caps.MinorType.OSMINORTYPE_WINDOWS_NT;
|
||||
generalCapability.extraFlags.value = caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED
|
||||
| caps.GeneralExtraFlag.NO_BITMAP_COMPRESSION_HDR
|
||||
| caps.GeneralExtraFlag.ENC_SALTED_CHECKSUM
|
||||
| caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED;
|
||||
|
||||
var bitmapCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_BITMAP].obj;
|
||||
bitmapCapability.preferredBitsPerPixel.value = this.gccCore.highColorDepth.value;
|
||||
bitmapCapability.desktopWidth.value = this.gccCore.desktopWidth.value;
|
||||
bitmapCapability.desktopHeight.value = this.gccCore.desktopHeight.value;
|
||||
|
||||
var orderCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_ORDER].obj;
|
||||
orderCapability.orderFlags.value |= caps.OrderFlag.ZEROBOUNDSDELTASSUPPORT;
|
||||
|
||||
var inputCapability = this.clientCapabilities[caps.CapsType.CAPSTYPE_INPUT].obj;
|
||||
inputCapability.inputFlags.value = caps.InputFlags.INPUT_FLAG_SCANCODES | caps.InputFlags.INPUT_FLAG_MOUSEX | caps.InputFlags.INPUT_FLAG_UNICODE;
|
||||
inputCapability.keyboardLayout = this.gccCore.kbdLayout;
|
||||
inputCapability.keyboardType = this.gccCore.keyboardType;
|
||||
inputCapability.keyboardSubType = this.gccCore.keyboardSubType;
|
||||
inputCapability.keyboardrFunctionKey = this.gccCore.keyboardFnKeys;
|
||||
inputCapability.imeFileName = this.gccCore.imeFileName;
|
||||
|
||||
var capabilities = new type.Component([]);
|
||||
for(var i in this.clientCapabilities) {
|
||||
capabilities.obj.push(caps.capability(this.clientCapabilities[i]));
|
||||
}
|
||||
|
||||
var confirmActivePDU = data.confirmActivePDU(capabilities, this.shareId);
|
||||
|
||||
this.sendPDU(confirmActivePDU);
|
||||
};
|
||||
|
||||
/**
|
||||
* send synchronize PDU
|
||||
*/
|
||||
Client.prototype.sendClientFinalizeSynchronizePDU = function() {
|
||||
this.sendDataPDU(data.synchronizeDataPDU(this.channelId));
|
||||
this.sendDataPDU(data.controlDataPDU(data.Action.CTRLACTION_COOPERATE));
|
||||
this.sendDataPDU(data.controlDataPDU(data.Action.CTRLACTION_REQUEST_CONTROL));
|
||||
this.sendDataPDU(data.fontListDataPDU());
|
||||
};
|
||||
|
||||
/**
|
||||
* Send input event as slow path input
|
||||
* @param inputEvents {array}
|
||||
*/
|
||||
Client.prototype.sendInputEvents = function (inputEvents) {
|
||||
var pdu = data.clientInputEventPDU(new type.Component(inputEvents.map(function (e) {
|
||||
return data.slowPathInputEvent(e);
|
||||
})));
|
||||
|
||||
this.sendDataPDU(pdu);
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
Client : Client
|
||||
};
|
||||
30
rdp/protocol/pdu/index.js
Normal file
30
rdp/protocol/pdu/index.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var lic = require('./lic');
|
||||
var sec = require('./sec');
|
||||
var global = require('./global');
|
||||
var data = require('./data');
|
||||
|
||||
module.exports = {
|
||||
lic : lic,
|
||||
sec : sec,
|
||||
global : global,
|
||||
data : data
|
||||
};
|
||||
309
rdp/protocol/pdu/lic.js
Normal file
309
rdp/protocol/pdu/lic.js
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../../core').type;
|
||||
|
||||
var MessageType = {
|
||||
LICENSE_REQUEST : 0x01,
|
||||
PLATFORM_CHALLENGE : 0x02,
|
||||
NEW_LICENSE : 0x03,
|
||||
UPGRADE_LICENSE : 0x04,
|
||||
LICENSE_INFO : 0x12,
|
||||
NEW_LICENSE_REQUEST : 0x13,
|
||||
PLATFORM_CHALLENGE_RESPONSE : 0x15,
|
||||
ERROR_ALERT : 0xFF
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
*/
|
||||
var ErrorCode = {
|
||||
ERR_INVALID_SERVER_CERTIFICATE : 0x00000001,
|
||||
ERR_NO_LICENSE : 0x00000002,
|
||||
ERR_INVALID_SCOPE : 0x00000004,
|
||||
ERR_NO_LICENSE_SERVER : 0x00000006,
|
||||
STATUS_VALID_CLIENT : 0x00000007,
|
||||
ERR_INVALID_CLIENT : 0x00000008,
|
||||
ERR_INVALID_PRODUCTID : 0x0000000B,
|
||||
ERR_INVALID_MESSAGE_LEN : 0x0000000C,
|
||||
ERR_INVALID_MAC : 0x00000003
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
*/
|
||||
var StateTransition = {
|
||||
ST_TOTAL_ABORT : 0x00000001,
|
||||
ST_NO_TRANSITION : 0x00000002,
|
||||
ST_RESET_PHASE_TO_START : 0x00000003,
|
||||
ST_RESEND_LAST_MESSAGE : 0x00000004
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||
*/
|
||||
var BinaryBlobType = {
|
||||
BB_ANY_BLOB : 0x0000,
|
||||
BB_DATA_BLOB : 0x0001,
|
||||
BB_RANDOM_BLOB : 0x0002,
|
||||
BB_CERTIFICATE_BLOB : 0x0003,
|
||||
BB_ERROR_BLOB : 0x0004,
|
||||
BB_ENCRYPTED_DATA_BLOB : 0x0009,
|
||||
BB_KEY_EXCHG_ALG_BLOB : 0x000D,
|
||||
BB_SCOPE_BLOB : 0x000E,
|
||||
BB_CLIENT_USER_NAME_BLOB : 0x000F,
|
||||
BB_CLIENT_MACHINE_NAME_BLOB : 0x0010
|
||||
};
|
||||
|
||||
var Preambule = {
|
||||
PREAMBLE_VERSION_2_0 : 0x2,
|
||||
PREAMBLE_VERSION_3_0 : 0x3,
|
||||
EXTENDED_ERROR_MSG_SUPPORTED : 0x80
|
||||
};
|
||||
|
||||
/**
|
||||
* Binary blob to emcompass license information
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||
* @param blobType {BinaryBlobType.*}
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function licenseBinaryBlob(blobType) {
|
||||
blobType = blobType || BinaryBlobType.BB_ANY_BLOB;
|
||||
var self = {
|
||||
wBlobType : new type.UInt16Le(blobType, { constant : (blobType === BinaryBlobType.BB_ANY_BLOB)?false:true }),
|
||||
wBlobLen : new type.UInt16Le(function() {
|
||||
return self.blobData.size();
|
||||
}),
|
||||
blobData : new type.BinaryString(null, { readLength : new type.CallableValue(function() {
|
||||
return self.wBlobLen.value;
|
||||
})})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Error message in license PDU automata
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function licensingErrorMessage(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.ERROR_ALERT,
|
||||
dwErrorCode : new type.UInt32Le(),
|
||||
dwStateTransition : new type.UInt32Le(),
|
||||
blob : licenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* License product informations
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241915.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function productInformation() {
|
||||
var self = {
|
||||
dwVersion : new type.UInt32Le(),
|
||||
cbCompanyName : new type.UInt32Le(function() {
|
||||
return self.pbCompanyName.size();
|
||||
}),
|
||||
// may contain "Microsoft Corporation" from server microsoft
|
||||
pbCompanyName : new type.BinaryString(Buffer.from('Microsoft Corporation', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbCompanyName.value;
|
||||
})}),
|
||||
cbProductId : new type.UInt32Le(function() {
|
||||
return self.pbProductId.size();
|
||||
}),
|
||||
// may contain "A02" from microsoft license server
|
||||
pbProductId : new type.BinaryString(Buffer.from('A02', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbProductId.value;
|
||||
})})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use in license negotiation
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241917.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function scope() {
|
||||
var self = {
|
||||
scope : licenseBinaryBlob(BinaryBlobType.BB_SCOPE_BLOB)
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241916.aspx
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function scopeList() {
|
||||
var self = {
|
||||
scopeCount : new type.UInt32Le(function() {
|
||||
return self.scopeArray.length;
|
||||
}),
|
||||
scopeArray : new type.Factory(function(s) {
|
||||
self.scopeArray = new type.Component([]);
|
||||
for(var i = 0; i < self.scopeCount.value; i++) {
|
||||
self.scopeArray.obj.push(scope().read(s));
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241914.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverLicenseRequest(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.LICENSE_REQUEST,
|
||||
serverRandom : new type.BinaryString(Buffer.from(Array(32 + 1).join('\x00')), { readLength : new type.CallableValue(32) } ),
|
||||
productInfo : productInformation(),
|
||||
keyExchangeList : licenseBinaryBlob(BinaryBlobType.BB_KEY_EXCHG_ALG_BLOB),
|
||||
serverCertificate : licenseBinaryBlob(BinaryBlobType.BB_CERTIFICATE_BLOB),
|
||||
scopeList : scopeList()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241918.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientNewLicenseRequest(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.NEW_LICENSE_REQUEST,
|
||||
preferredKeyExchangeAlg : new type.UInt32Le(0x00000001, { constant : true }),
|
||||
// pure microsoft client ;-)
|
||||
// http://msdn.microsoft.com/en-us/library/1040af38-c733-4fb3-acd1-8db8cc979eda#id10
|
||||
platformId : new type.UInt32Le(0x04000000 | 0x00010000),
|
||||
clientRandom : new type.BinaryString(Buffer.from(Array(32 + 1).join('\x00')), { readLength : new type.CallableValue(32) }),
|
||||
encryptedPreMasterSecret : licenseBinaryBlob(BinaryBlobType.BB_RANDOM_BLOB),
|
||||
ClientUserName : licenseBinaryBlob(BinaryBlobType.BB_CLIENT_USER_NAME_BLOB),
|
||||
ClientMachineName : licenseBinaryBlob(BinaryBlobType.BB_CLIENT_MACHINE_NAME_BLOB)
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241921.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverPlatformChallenge(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.PLATFORM_CHALLENGE,
|
||||
connectFlags : new type.UInt32Le(),
|
||||
encryptedPlatformChallenge : licenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB),
|
||||
MACData : new type.BinaryString(Buffer.from(Array(16 + 1).join('\x00')), { readLength : new type.CallableValue(16) })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241922.aspx
|
||||
* @param opt {object} type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientPLatformChallengeResponse(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.PLATFORM_CHALLENGE_RESPONSE,
|
||||
encryptedPlatformChallengeResponse : licenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB),
|
||||
encryptedHWID : licenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB),
|
||||
MACData : new type.BinaryString(Buffer.from(Array(16 + 1).join('\x00'), 'binary'), { readLength : new type.CallableValue(16) })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
};
|
||||
|
||||
/**
|
||||
* Global license packet
|
||||
* @param packet {type.* | null} send packet
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function licensePacket(message) {
|
||||
var self = {
|
||||
bMsgtype : new type.UInt8(function() {
|
||||
return self.licensingMessage.obj.__TYPE__;
|
||||
}),
|
||||
flag : new type.UInt8(Preambule.PREAMBLE_VERSION_3_0),
|
||||
wMsgSize : new type.UInt16Le(function() {
|
||||
return new type.Component(self).size();
|
||||
}),
|
||||
licensingMessage : message || new type.Factory(function(s) {
|
||||
switch(self.bMsgtype.value) {
|
||||
case MessageType.ERROR_ALERT:
|
||||
self.licensingMessage = licensingErrorMessage({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.LICENSE_REQUEST:
|
||||
self.licensingMessage = serverLicenseRequest({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.NEW_LICENSE_REQUEST:
|
||||
self.licensingMessage = clientNewLicenseRequest({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.PLATFORM_CHALLENGE:
|
||||
self.licensingMessage = serverPlatformChallenge({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
case MessageType.PLATFORM_CHALLENGE_RESPONSE:
|
||||
self.licensingMessage = clientPLatformChallengeResponse({ readLength : new type.CallableValue(function() {
|
||||
return self.wMsgSize.value - 4;
|
||||
})}).read(s);
|
||||
break;
|
||||
default:
|
||||
log.error('unknown license message type ' + self.bMsgtype.value);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
MessageType : MessageType,
|
||||
ErrorCode : ErrorCode,
|
||||
StateTransition : StateTransition,
|
||||
licensePacket : licensePacket,
|
||||
clientNewLicenseRequest : clientNewLicenseRequest,
|
||||
clientPLatformChallengeResponse : clientPLatformChallengeResponse
|
||||
};
|
||||
519
rdp/protocol/pdu/sec.js
Normal file
519
rdp/protocol/pdu/sec.js
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var crypto = require('crypto');
|
||||
var events = require('events');
|
||||
var type = require('../../core').type;
|
||||
var error = require('../../core').error;
|
||||
var log = require('../../core').log;
|
||||
var gcc = require('../t125/gcc');
|
||||
var lic = require('./lic');
|
||||
var cert = require('../cert');
|
||||
var rsa = require('../../security').rsa;
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240579.aspx
|
||||
*/
|
||||
var SecurityFlag = {
|
||||
SEC_EXCHANGE_PKT : 0x0001,
|
||||
SEC_TRANSPORT_REQ : 0x0002,
|
||||
RDP_SEC_TRANSPORT_RSP : 0x0004,
|
||||
SEC_ENCRYPT : 0x0008,
|
||||
SEC_RESET_SEQNO : 0x0010,
|
||||
SEC_IGNORE_SEQNO : 0x0020,
|
||||
SEC_INFO_PKT : 0x0040,
|
||||
SEC_LICENSE_PKT : 0x0080,
|
||||
SEC_LICENSE_ENCRYPT_CS : 0x0200,
|
||||
SEC_LICENSE_ENCRYPT_SC : 0x0200,
|
||||
SEC_REDIRECTION_PKT : 0x0400,
|
||||
SEC_SECURE_CHECKSUM : 0x0800,
|
||||
SEC_AUTODETECT_REQ : 0x1000,
|
||||
SEC_AUTODETECT_RSP : 0x2000,
|
||||
SEC_HEARTBEAT : 0x4000,
|
||||
SEC_FLAGSHI_VALID : 0x8000
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://msdn.microsoft.com/en-us/library/cc240475.aspx
|
||||
*/
|
||||
var InfoFlag = {
|
||||
INFO_MOUSE : 0x00000001,
|
||||
INFO_DISABLECTRLALTDEL : 0x00000002,
|
||||
INFO_AUTOLOGON : 0x00000008,
|
||||
INFO_UNICODE : 0x00000010,
|
||||
INFO_MAXIMIZESHELL : 0x00000020,
|
||||
INFO_LOGONNOTIFY : 0x00000040,
|
||||
INFO_COMPRESSION : 0x00000080,
|
||||
INFO_ENABLEWINDOWSKEY : 0x00000100,
|
||||
INFO_REMOTECONSOLEAUDIO : 0x00002000,
|
||||
INFO_FORCE_ENCRYPTED_CS_PDU : 0x00004000,
|
||||
INFO_RAIL : 0x00008000,
|
||||
INFO_LOGONERRORS : 0x00010000,
|
||||
INFO_MOUSE_HAS_WHEEL : 0x00020000,
|
||||
INFO_PASSWORD_IS_SC_PIN : 0x00040000,
|
||||
INFO_NOAUDIOPLAYBACK : 0x00080000,
|
||||
INFO_USING_SAVED_CREDS : 0x00100000,
|
||||
INFO_AUDIOCAPTURE : 0x00200000,
|
||||
INFO_VIDEO_DISABLE : 0x00400000,
|
||||
INFO_CompressionTypeMask : 0x00001E00
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://msdn.microsoft.com/en-us/library/cc240476.aspx
|
||||
*/
|
||||
var AfInet = {
|
||||
AfInet : 0x00002,
|
||||
AF_INET6 : 0x0017
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://msdn.microsoft.com/en-us/library/cc240476.aspx
|
||||
*/
|
||||
var PerfFlag = {
|
||||
PERF_DISABLE_WALLPAPER : 0x00000001,
|
||||
PERF_DISABLE_FULLWINDOWDRAG : 0x00000002,
|
||||
PERF_DISABLE_MENUANIMATIONS : 0x00000004,
|
||||
PERF_DISABLE_THEMING : 0x00000008,
|
||||
PERF_DISABLE_CURSOR_SHADOW : 0x00000020,
|
||||
PERF_DISABLE_CURSORSETTINGS : 0x00000040,
|
||||
PERF_ENABLE_FONT_SMOOTHING : 0x00000080,
|
||||
PERF_ENABLE_DESKTOP_COMPOSITION : 0x00000100
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241992.aspx
|
||||
* @param input {Buffer} Binary data
|
||||
* @param salt {Buffer} salt for context call
|
||||
* @param salt1 {Buffer} another salt (ex : client random)
|
||||
* @param salt2 {Buffer} another salt (ex : server random)
|
||||
* @return {Buffer}
|
||||
*/
|
||||
function saltedHash(input, salt, salt1, salt2) {
|
||||
var sha1Digest = crypto.createHash('sha1');
|
||||
sha1Digest.update(input);
|
||||
sha1Digest.update(salt.slice(0, 48));
|
||||
sha1Digest.update(salt1);
|
||||
sha1Digest.update(salt2);
|
||||
|
||||
var sha1Sig = sha1Digest.digest();
|
||||
|
||||
var md5Digest = crypto.createHash('md5');
|
||||
md5Digest.update(salt.slice(0, 48));
|
||||
md5Digest.update(sha1Sig);
|
||||
return md5Digest.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key {Buffer} secret
|
||||
* @param random1 {Buffer} client random
|
||||
* @param random2 {Buffer} server random
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function finalHash (key, random1, random2) {
|
||||
var md5Digest = crypto.createHash('md5');
|
||||
md5Digest.update(key);
|
||||
md5Digest.update(random1);
|
||||
md5Digest.update(random2);
|
||||
return md5Digest.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241992.aspx
|
||||
* @param secret {Buffer} secret
|
||||
* @param random1 {Buffer} client random
|
||||
* @param random2 {Buffer} server random
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function masterSecret (secret, random1, random2) {
|
||||
var sh1 = saltedHash(Buffer.from('A'), secret, random1, random2);
|
||||
var sh2 = saltedHash(Buffer.from('BB'), secret, random1, random2);
|
||||
var sh3 = saltedHash(Buffer.from('CCC'), secret, random1, random2);
|
||||
|
||||
var ms = Buffer.alloc(sh1.length + sh2.length + sh3.length);
|
||||
sh1.copy(ms);
|
||||
sh2.copy(ms, sh1.length);
|
||||
sh3.copy(ms, sh1.length + sh2.length);
|
||||
return ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc241995.aspx
|
||||
* @param macSaltKey {Buffer} key
|
||||
* @param data {Buffer} data
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
function macData(macSaltKey, data) {
|
||||
var salt1 = Buffer.alloc(40);
|
||||
salt1.fill(0x36);
|
||||
|
||||
var salt2 = Buffer.alloc(48);
|
||||
salt2.fill(0x5c);
|
||||
|
||||
var dataLength = new type.UInt32Le(data.length).toStream().buffer;
|
||||
|
||||
var sha1 = crypto.createHash('sha1');
|
||||
sha1.update(macSaltKey);
|
||||
sha1.update(salt1);
|
||||
sha1.update(dataLength);
|
||||
sha1.update(data);
|
||||
var sha1Digest = sha1.digest();
|
||||
|
||||
var md5 = crypto.createHash('md5');
|
||||
md5.update(macSaltKey);
|
||||
md5.update(salt2);
|
||||
md5.update(sha1Digest);
|
||||
|
||||
return md5.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* RDP client informations
|
||||
* @param extendedInfoConditional {boolean} true if RDP5+
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function rdpInfos(extendedInfoConditional) {
|
||||
var self = {
|
||||
codePage : new type.UInt32Le(),
|
||||
flag : new type.UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_DISABLECTRLALTDEL | InfoFlag.INFO_ENABLEWINDOWSKEY),
|
||||
cbDomain : new type.UInt16Le(function() {
|
||||
return self.domain.size() - 2;
|
||||
}),
|
||||
cbUserName : new type.UInt16Le(function() {
|
||||
return self.userName.size() - 2;
|
||||
}),
|
||||
cbPassword : new type.UInt16Le(function() {
|
||||
return self.password.size() - 2;
|
||||
}),
|
||||
cbAlternateShell : new type.UInt16Le(function() {
|
||||
return self.alternateShell.size() - 2;
|
||||
}),
|
||||
cbWorkingDir : new type.UInt16Le(function() {
|
||||
return self.workingDir.size() - 2;
|
||||
}),
|
||||
domain : new type.BinaryString(Buffer.from('\x00', 'ucs2'),{ readLength : new type.CallableValue(function() {
|
||||
return self.cbDomain.value + 2;
|
||||
})}),
|
||||
userName : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbUserName.value + 2;
|
||||
})}),
|
||||
password : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function () {
|
||||
return self.cbPassword.value + 2;
|
||||
})}),
|
||||
alternateShell : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbAlternateShell.value + 2;
|
||||
})}),
|
||||
workingDir : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbWorkingDir.value + 2;
|
||||
})}),
|
||||
extendedInfo : rdpExtendedInfos({ conditional : extendedInfoConditional })
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* RDP client extended informations present in RDP5+
|
||||
* @param opt
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function rdpExtendedInfos(opt) {
|
||||
var self = {
|
||||
clientAddressFamily : new type.UInt16Le(AfInet.AfInet),
|
||||
cbClientAddress : new type.UInt16Le(function() {
|
||||
return self.clientAddress.size();
|
||||
}),
|
||||
clientAddress : new type.BinaryString(Buffer.from('\x00', 'ucs2'),{ readLength : new type.CallableValue(function() {
|
||||
return self.cbClientAddress;
|
||||
}) }),
|
||||
cbClientDir : new type.UInt16Le(function() {
|
||||
return self.clientDir.size();
|
||||
}),
|
||||
clientDir : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
|
||||
return self.cbClientDir;
|
||||
}) }),
|
||||
clientTimeZone : new type.BinaryString(Buffer.from(Array(172 + 1).join("\x00"))),
|
||||
clientSessionId : new type.UInt32Le(),
|
||||
performanceFlags : new type.UInt32Le()
|
||||
};
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Header of security header
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function securityHeader() {
|
||||
var self = {
|
||||
securityFlag : new type.UInt16Le(),
|
||||
securityFlagHi : new type.UInt16Le()
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Security layer
|
||||
* @param transport {events.EventEmitter}
|
||||
*/
|
||||
function Sec(transport, fastPathTransport) {
|
||||
this.transport = transport;
|
||||
this.fastPathTransport = fastPathTransport;
|
||||
// init at connect event from transport layer
|
||||
this.gccClient = null;
|
||||
this.gccServer = null;
|
||||
var self = this;
|
||||
this.infos = rdpInfos(function() {
|
||||
return self.gccClient.core.rdpVersion.value === gcc.VERSION.RDP_VERSION_5_PLUS;
|
||||
});
|
||||
this.machineName = '';
|
||||
|
||||
|
||||
// basic encryption
|
||||
this.enableEncryption = false;
|
||||
|
||||
if (this.fastPathTransport) {
|
||||
this.fastPathTransport.on('fastPathData', function (secFlag, s) {
|
||||
self.recvFastPath(secFlag, s);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//inherit from Layer
|
||||
inherits(Sec, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Send message with security header
|
||||
* @param flag {integer} security flag
|
||||
* @param data {type.*} message
|
||||
*/
|
||||
Sec.prototype.sendFlagged = function(flag, data) {
|
||||
this.transport.send('global', new type.Component([
|
||||
new type.UInt16Le(flag),
|
||||
new type.UInt16Le(),
|
||||
data
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Main send function
|
||||
* @param message {type.*} message to send
|
||||
*/
|
||||
Sec.prototype.send = function(message) {
|
||||
if (this.enableEncryption) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_SEC_ENCRYPT_NOT_IMPLEMENTED');
|
||||
}
|
||||
this.transport.send('global', message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Main receive function
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Sec.prototype.recv = function(s) {
|
||||
if (this.enableEncryption) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_SEC_ENCRYPT_NOT_IMPLEMENTED');
|
||||
}
|
||||
// not support yet basic RDP security layer
|
||||
this.emit('data', s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive fast path data
|
||||
* @param secFlag {integer} security flag
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Sec.prototype.recvFastPath = function (secFlag, s) {
|
||||
// transparent because basic RDP security layer not implemented
|
||||
this.emit('fastPathData', secFlag, s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Client security layer
|
||||
* @param transport {events.EventEmitter}
|
||||
*/
|
||||
function Client(transport, fastPathTransport) {
|
||||
Sec.call(this, transport, fastPathTransport);
|
||||
// for basic RDP layer (in futur)
|
||||
this.enableSecureCheckSum = false;
|
||||
var self = this;
|
||||
this.transport.on('connect', function(gccClient, gccServer, userId, channels) {
|
||||
self.connect(gccClient, gccServer, userId, channels);
|
||||
}).on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
};
|
||||
|
||||
//inherit from Layer
|
||||
inherits(Client, Sec);
|
||||
|
||||
/**
|
||||
* Connect event
|
||||
*/
|
||||
Client.prototype.connect = function(gccClient, gccServer, userId, channels) {
|
||||
//init gcc information
|
||||
this.gccClient = gccClient;
|
||||
this.gccServer = gccServer;
|
||||
this.userId = userId;
|
||||
this.channelId = channels.find(function(e) {
|
||||
if(e.name === 'global') return true;
|
||||
}).id;
|
||||
this.sendInfoPkt();
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Send main information packet
|
||||
* VIP (very important packet) because contain credentials
|
||||
*/
|
||||
Client.prototype.sendInfoPkt = function() {
|
||||
this.sendFlagged(SecurityFlag.SEC_INFO_PKT, this.infos);
|
||||
var self = this;
|
||||
this.transport.once('global', function(s) {
|
||||
self.recvLicense(s);
|
||||
});
|
||||
};
|
||||
|
||||
function reverse(buffer) {
|
||||
var result = Buffer.alloc(buffer.length);
|
||||
for(var i = 0; i < buffer.length; i++) {
|
||||
result.writeUInt8(buffer.readUInt8(buffer.length - 1 - i), i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a valid license request
|
||||
* @param licenseRequest {object(lic.serverLicenseRequest)} license requets infos
|
||||
*/
|
||||
Client.prototype.sendClientNewLicenseRequest = function(licenseRequest) {
|
||||
log.debug('new license request');
|
||||
var serverRandom = licenseRequest.serverRandom.value;
|
||||
|
||||
// read server certificate
|
||||
var s = new type.Stream(licenseRequest.serverCertificate.obj.blobData.value);
|
||||
var certificate = cert.certificate().read(s).obj;
|
||||
var publicKey = certificate.certData.obj.getPublicKey();
|
||||
|
||||
var clientRandom = crypto.randomBytes(32);
|
||||
var preMasterSecret = crypto.randomBytes(48);
|
||||
var mSecret = masterSecret(preMasterSecret, clientRandom, serverRandom);
|
||||
var sessionKeyBlob = masterSecret(mSecret, serverRandom, clientRandom);
|
||||
|
||||
this.licenseMacSalt = sessionKeyBlob.slice(0, 16)
|
||||
this.licenseKey = finalHash(sessionKeyBlob.slice(16, 32), clientRandom, serverRandom);
|
||||
|
||||
var request = lic.clientNewLicenseRequest();
|
||||
request.obj.clientRandom.value = clientRandom;
|
||||
|
||||
var preMasterSecretEncrypted = reverse(rsa.encrypt(reverse(preMasterSecret), publicKey));
|
||||
var preMasterSecretEncryptedPadded = Buffer.alloc(preMasterSecretEncrypted.length + 8);
|
||||
preMasterSecretEncryptedPadded.fill(0);
|
||||
preMasterSecretEncrypted.copy(preMasterSecretEncryptedPadded);
|
||||
request.obj.encryptedPreMasterSecret.obj.blobData.value = preMasterSecretEncryptedPadded;
|
||||
|
||||
request.obj.ClientMachineName.obj.blobData.value = this.infos.obj.userName.value;
|
||||
request.obj.ClientUserName.obj.blobData.value = Buffer.from(this.machineName + '\x00');
|
||||
|
||||
this.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.licensePacket(request));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a valid license request
|
||||
* @param platformChallenge {object(lic.serverPlatformChallenge)} platform challenge
|
||||
*/
|
||||
Client.prototype.sendClientChallengeResponse = function(platformChallenge) {
|
||||
log.debug('challenge license');
|
||||
var serverEncryptedChallenge = platformChallenge.encryptedPlatformChallenge.obj.blobData.value;
|
||||
var serverChallenge = crypto.createDecipheriv('rc4', this.licenseKey, '').update(serverEncryptedChallenge);
|
||||
if (serverChallenge.toString('ucs2') !== 'TEST\x00') {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_PDU_SEC_INVALID_LICENSE_CHALLENGE');
|
||||
}
|
||||
|
||||
var hwid = new type.Component([new type.UInt32Le(2), new type.BinaryString(crypto.randomBytes(16))]).toStream().buffer;
|
||||
|
||||
var response = lic.clientPLatformChallengeResponse();
|
||||
response.obj.encryptedPlatformChallengeResponse.obj.blobData.value = serverEncryptedChallenge;
|
||||
response.obj.encryptedHWID.obj.blobData.value = crypto.createCipheriv('rc4', this.licenseKey, '').update(hwid);
|
||||
|
||||
var sig = Buffer.alloc(serverChallenge.length + hwid.length);
|
||||
serverChallenge.copy(sig);
|
||||
hwid.copy(sig, serverChallenge.length);
|
||||
response.obj.MACData.value = macData(this.licenseMacSalt, sig);
|
||||
|
||||
this.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.licensePacket(response));
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive license informations
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Sec.prototype.recvLicense = function(s) {
|
||||
var header = securityHeader().read(s).obj;
|
||||
if (!(header.securityFlag.value & SecurityFlag.SEC_LICENSE_PKT)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER');
|
||||
}
|
||||
|
||||
var message = lic.licensePacket().read(s).obj;
|
||||
// i'm accepted
|
||||
if (message.bMsgtype.value === lic.MessageType.NEW_LICENSE ||
|
||||
(message.bMsgtype.value === lic.MessageType.ERROR_ALERT
|
||||
&& message.licensingMessage.obj.dwErrorCode.value === lic.ErrorCode.STATUS_VALID_CLIENT
|
||||
&& message.licensingMessage.obj.dwStateTransition.value === lic.StateTransition.ST_NO_TRANSITION)) {
|
||||
this.emit('connect', this.gccClient.core, this.userId, this.channelId);
|
||||
var self = this;
|
||||
this.transport.on('global', function(s) {
|
||||
self.recv(s);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// server ask license request
|
||||
if (message.bMsgtype.value === lic.MessageType.LICENSE_REQUEST) {
|
||||
this.sendClientNewLicenseRequest(message.licensingMessage.obj);
|
||||
}
|
||||
|
||||
// server send challenge
|
||||
if (message.bMsgtype.value === lic.MessageType.PLATFORM_CHALLENGE) {
|
||||
this.sendClientChallengeResponse(message.licensingMessage.obj);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.emit('connect', this.gccClient.core);
|
||||
this.transport.once('global', function (s) {
|
||||
self.recvLicense(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
PerfFlag : PerfFlag,
|
||||
InfoFlag : InfoFlag,
|
||||
Client : Client
|
||||
};
|
||||
361
rdp/protocol/rdp.js
Normal file
361
rdp/protocol/rdp.js
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var net = require('net');
|
||||
var inherits = require('util').inherits;
|
||||
var events = require('events');
|
||||
var layer = require('../core').layer;
|
||||
var error = require('../core').error;
|
||||
var rle = require('../core').rle;
|
||||
var log = require('../core').log;
|
||||
var TPKT = require('./tpkt');
|
||||
var x224 = require('./x224');
|
||||
var t125 = require('./t125');
|
||||
var pdu = require('./pdu');
|
||||
|
||||
/**
|
||||
* decompress bitmap from RLE algorithm
|
||||
* @param bitmap {object} bitmap object of bitmap event of node-rdpjs
|
||||
*/
|
||||
function decompress (bitmap) {
|
||||
var fName = null;
|
||||
switch (bitmap.bitsPerPixel.value) {
|
||||
case 15:
|
||||
fName = 'bitmap_decompress_15';
|
||||
break;
|
||||
case 16:
|
||||
fName = 'bitmap_decompress_16';
|
||||
break;
|
||||
case 24:
|
||||
fName = 'bitmap_decompress_24';
|
||||
break;
|
||||
case 32:
|
||||
fName = 'bitmap_decompress_32';
|
||||
break;
|
||||
default:
|
||||
throw 'invalid bitmap data format';
|
||||
}
|
||||
|
||||
var input = new Uint8Array(bitmap.bitmapDataStream.value);
|
||||
var inputPtr = rle._malloc(input.length);
|
||||
var inputHeap = new Uint8Array(rle.HEAPU8.buffer, inputPtr, input.length);
|
||||
inputHeap.set(input);
|
||||
|
||||
var ouputSize = bitmap.width.value * bitmap.height.value * 4;
|
||||
var outputPtr = rle._malloc(ouputSize);
|
||||
|
||||
var outputHeap = new Uint8Array(rle.HEAPU8.buffer, outputPtr, ouputSize);
|
||||
|
||||
var res = rle.ccall(fName,
|
||||
'number',
|
||||
['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
|
||||
[outputHeap.byteOffset, bitmap.width.value, bitmap.height.value, bitmap.width.value, bitmap.height.value, inputHeap.byteOffset, input.length]
|
||||
);
|
||||
|
||||
var output = new Uint8ClampedArray(outputHeap.buffer, outputHeap.byteOffset, ouputSize);
|
||||
|
||||
rle._free(inputPtr);
|
||||
rle._free(outputPtr);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main RDP module
|
||||
*/
|
||||
function RdpClient(config) {
|
||||
config = config || {};
|
||||
this.connected = false;
|
||||
this.bufferLayer = new layer.BufferLayer(new net.Socket());
|
||||
this.tpkt = new TPKT(this.bufferLayer);
|
||||
this.x224 = new x224.Client(this.tpkt, config);
|
||||
this.mcs = new t125.mcs.Client(this.x224);
|
||||
this.sec = new pdu.sec.Client(this.mcs, this.tpkt);
|
||||
this.global = new pdu.global.Client(this.sec, this.sec);
|
||||
|
||||
// config log level
|
||||
log.level = log.Levels[config.logLevel || 'INFO'] || log.Levels.INFO;
|
||||
|
||||
// credentials
|
||||
if (config.domain) {
|
||||
this.sec.infos.obj.domain.value = Buffer.from(config.domain + '\x00', 'ucs2');
|
||||
}
|
||||
if (config.userName) {
|
||||
this.sec.infos.obj.userName.value = Buffer.from(config.userName + '\x00', 'ucs2');
|
||||
}
|
||||
if (config.password) {
|
||||
this.sec.infos.obj.password.value = Buffer.from(config.password + '\x00', 'ucs2');
|
||||
}
|
||||
if(config.workingDir) {
|
||||
this.sec.infos.obj.workingDir.value = Buffer.from(config.workingDir + '\x00', 'ucs2');
|
||||
}
|
||||
if(config.alternateShell) {
|
||||
this.sec.infos.obj.alternateShell.value = Buffer.from(config.alternateShell + '\x00', 'ucs2');
|
||||
}
|
||||
|
||||
if (config.enablePerf) {
|
||||
this.sec.infos.obj.extendedInfo.obj.performanceFlags.value =
|
||||
pdu.sec.PerfFlag.PERF_DISABLE_WALLPAPER
|
||||
| pdu.sec.PerfFlag.PERF_DISABLE_MENUANIMATIONS
|
||||
| pdu.sec.PerfFlag.PERF_DISABLE_CURSOR_SHADOW
|
||||
| pdu.sec.PerfFlag.PERF_DISABLE_THEMING
|
||||
| pdu.sec.PerfFlag.PERF_DISABLE_FULLWINDOWDRAG;
|
||||
}
|
||||
|
||||
if (config.autoLogin) {
|
||||
this.sec.infos.obj.flag.value |= pdu.sec.InfoFlag.INFO_AUTOLOGON;
|
||||
}
|
||||
|
||||
if (config.screen && config.screen.width && config.screen.height) {
|
||||
this.mcs.clientCoreData.obj.desktopWidth.value = config.screen.width;
|
||||
this.mcs.clientCoreData.obj.desktopHeight.value = config.screen.height;
|
||||
}
|
||||
|
||||
log.debug('screen ' + this.mcs.clientCoreData.obj.desktopWidth.value + 'x' + this.mcs.clientCoreData.obj.desktopHeight.value);
|
||||
|
||||
// config keyboard layout
|
||||
switch (config.locale) {
|
||||
case 'fr':
|
||||
log.debug('french keyboard layout');
|
||||
this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.FRENCH;
|
||||
break;
|
||||
case 'en':
|
||||
default:
|
||||
log.debug('english keyboard layout');
|
||||
this.mcs.clientCoreData.obj.kbdLayout.value = t125.gcc.KeyboardLayout.US;
|
||||
}
|
||||
|
||||
|
||||
//bind all events
|
||||
var self = this;
|
||||
this.global.on('connect', function () {
|
||||
self.connected = true;
|
||||
self.emit('connect');
|
||||
}).on('session', function () {
|
||||
self.emit('session');
|
||||
}).on('close', function () {
|
||||
self.connected = false;
|
||||
self.emit('close');
|
||||
}).on('bitmap', function (bitmaps) {
|
||||
for(var bitmap in bitmaps) {
|
||||
var bitmapData = bitmaps[bitmap].obj.bitmapDataStream.value;
|
||||
var isCompress = bitmaps[bitmap].obj.flags.value & pdu.data.BitmapFlag.BITMAP_COMPRESSION;
|
||||
|
||||
if (isCompress && config.decompress) {
|
||||
bitmapData = decompress(bitmaps[bitmap].obj);
|
||||
isCompress = false;
|
||||
}
|
||||
|
||||
self.emit('bitmap', {
|
||||
destTop : bitmaps[bitmap].obj.destTop.value,
|
||||
destLeft : bitmaps[bitmap].obj.destLeft.value,
|
||||
destBottom : bitmaps[bitmap].obj.destBottom.value,
|
||||
destRight : bitmaps[bitmap].obj.destRight.value,
|
||||
width : bitmaps[bitmap].obj.width.value,
|
||||
height : bitmaps[bitmap].obj.height.value,
|
||||
bitsPerPixel : bitmaps[bitmap].obj.bitsPerPixel.value,
|
||||
isCompress : isCompress,
|
||||
data : bitmapData
|
||||
});
|
||||
}
|
||||
}).on('error', function (err) {
|
||||
log.warn(err.code + '(' + err.message + ')\n' + err.stack);
|
||||
if (err instanceof error.FatalError) {
|
||||
throw err;
|
||||
}
|
||||
else {
|
||||
self.emit('error', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
inherits(RdpClient, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Connect RDP client
|
||||
* @param host {string} destination host
|
||||
* @param port {integer} destination port
|
||||
*/
|
||||
RdpClient.prototype.connect = function (host, port) {
|
||||
log.debug('connect to ' + host + ':' + port);
|
||||
var self = this;
|
||||
this.bufferLayer.socket.connect(port, host, function () {
|
||||
// in client mode connection start from x224 layer
|
||||
self.x224.connect();
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Close RDP client
|
||||
*/
|
||||
RdpClient.prototype.close = function () {
|
||||
if(this.connected) {
|
||||
this.global.close();
|
||||
}
|
||||
this.connected = false;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send pointer event to server
|
||||
* @param x {integer} mouse x position
|
||||
* @param y {integer} mouse y position
|
||||
* @param button {integer} button number of mouse
|
||||
* @param isPressed {boolean} state of button
|
||||
*/
|
||||
RdpClient.prototype.sendPointerEvent = function (x, y, button, isPressed) {
|
||||
if (!this.connected)
|
||||
return;
|
||||
|
||||
var event = pdu.data.pointerEvent();
|
||||
if (isPressed) {
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_DOWN;
|
||||
}
|
||||
|
||||
switch(button) {
|
||||
case 1:
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON1;
|
||||
break;
|
||||
case 2:
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON2;
|
||||
break;
|
||||
case 3:
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_BUTTON3
|
||||
break;
|
||||
default:
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_MOVE;
|
||||
}
|
||||
|
||||
event.obj.xPos.value = x;
|
||||
event.obj.yPos.value = y;
|
||||
|
||||
this.global.sendInputEvents([event]);
|
||||
};
|
||||
|
||||
/**
|
||||
* send scancode event
|
||||
* @param code {integer}
|
||||
* @param isPressed {boolean}
|
||||
* @param extended {boolenan} extended keys
|
||||
*/
|
||||
RdpClient.prototype.sendKeyEventScancode = function (code, isPressed, extended) {
|
||||
if (!this.connected)
|
||||
return;
|
||||
extended = extended || false;
|
||||
var event = pdu.data.scancodeKeyEvent();
|
||||
event.obj.keyCode.value = code;
|
||||
|
||||
if (!isPressed) {
|
||||
event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE;
|
||||
}
|
||||
|
||||
if (extended) {
|
||||
event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED;
|
||||
}
|
||||
|
||||
this.global.sendInputEvents([event]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send key event as unicode
|
||||
* @param code {integer}
|
||||
* @param isPressed {boolean}
|
||||
*/
|
||||
RdpClient.prototype.sendKeyEventUnicode = function (code, isPressed) {
|
||||
if (!this.connected)
|
||||
return;
|
||||
|
||||
var event = pdu.data.unicodeKeyEvent();
|
||||
event.obj.unicode.value = code;
|
||||
|
||||
if (!isPressed) {
|
||||
event.obj.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE;
|
||||
}
|
||||
this.global.sendInputEvents([event]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wheel mouse event
|
||||
* @param x {integer} mouse x position
|
||||
* @param y {integer} mouse y position
|
||||
* @param step {integer} wheel step
|
||||
* @param isNegative {boolean}
|
||||
* @param isHorizontal {boolean}
|
||||
*/
|
||||
RdpClient.prototype.sendWheelEvent = function (x, y, step, isNegative, isHorizontal) {
|
||||
if (!this.connected)
|
||||
return;
|
||||
|
||||
var event = pdu.data.pointerEvent();
|
||||
if (isHorizontal) {
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_HWHEEL;
|
||||
}
|
||||
else {
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL;
|
||||
}
|
||||
|
||||
|
||||
if (isNegative) {
|
||||
event.obj.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL_NEGATIVE;
|
||||
}
|
||||
|
||||
event.obj.pointerFlags.value |= (step & pdu.data.PointerFlag.WheelRotationMask)
|
||||
|
||||
event.obj.xPos.value = x;
|
||||
event.obj.yPos.value = y;
|
||||
|
||||
this.global.sendInputEvents([event]);
|
||||
}
|
||||
|
||||
function createClient(config) {
|
||||
return new RdpClient(config);
|
||||
};
|
||||
|
||||
/**
|
||||
* RDP server side protocol
|
||||
* @param config {object} configuration
|
||||
* @param socket {net.Socket}
|
||||
*/
|
||||
function RdpServer(config, socket) {
|
||||
if (!(config.key && config.cert)) {
|
||||
throw new error.FatalError('NODE_RDP_PROTOCOL_RDP_SERVER_CONFIG_MISSING', 'missing cryptographic tools')
|
||||
}
|
||||
this.connected = false;
|
||||
this.bufferLayer = new layer.BufferLayer(socket);
|
||||
this.tpkt = new TPKT(this.bufferLayer);
|
||||
this.x224 = new x224.Server(this.tpkt, config.key, config.cert);
|
||||
this.mcs = new t125.mcs.Server(this.x224);
|
||||
};
|
||||
|
||||
inherits(RdpServer, events.EventEmitter);
|
||||
|
||||
function createServer (config, next) {
|
||||
return net.createServer(function (socket) {
|
||||
next(new RdpServer(config, socket));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
createClient : createClient,
|
||||
createServer : createServer
|
||||
};
|
||||
540
rdp/protocol/t125/gcc.js
Normal file
540
rdp/protocol/t125/gcc.js
Normal file
|
|
@ -0,0 +1,540 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
var error = require('../../core').error;
|
||||
var per = require('./per');
|
||||
|
||||
|
||||
var t124_02_98_oid = [ 0, 0, 20, 124, 0, 1 ];
|
||||
var h221_cs_key = "Duca";
|
||||
var h221_sc_key = "McDn";
|
||||
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240509.aspx
|
||||
*/
|
||||
var MessageType = {
|
||||
//server -> client
|
||||
SC_CORE : 0x0C01,
|
||||
SC_SECURITY : 0x0C02,
|
||||
SC_NET : 0x0C03,
|
||||
//client -> server
|
||||
CS_CORE : 0xC001,
|
||||
CS_SECURITY : 0xC002,
|
||||
CS_NET : 0xC003,
|
||||
CS_CLUSTER : 0xC004,
|
||||
CS_MONITOR : 0xC005
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var ColorDepth = {
|
||||
RNS_UD_COLOR_8BPP : 0xCA01,
|
||||
RNS_UD_COLOR_16BPP_555 : 0xCA02,
|
||||
RNS_UD_COLOR_16BPP_565 : 0xCA03,
|
||||
RNS_UD_COLOR_24BPP : 0xCA04
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var HighColor = {
|
||||
HIGH_COLOR_4BPP : 0x0004,
|
||||
HIGH_COLOR_8BPP : 0x0008,
|
||||
HIGH_COLOR_15BPP : 0x000f,
|
||||
HIGH_COLOR_16BPP : 0x0010,
|
||||
HIGH_COLOR_24BPP : 0x0018
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var Support = {
|
||||
RNS_UD_24BPP_SUPPORT : 0x0001,
|
||||
RNS_UD_16BPP_SUPPORT : 0x0002,
|
||||
RNS_UD_15BPP_SUPPORT : 0x0004,
|
||||
RNS_UD_32BPP_SUPPORT : 0x0008
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var CapabilityFlag = {
|
||||
RNS_UD_CS_SUPPORT_ERRINFO_PDU : 0x0001,
|
||||
RNS_UD_CS_WANT_32BPP_SESSION : 0x0002,
|
||||
RNS_UD_CS_SUPPORT_STATUSINFO_PDU : 0x0004,
|
||||
RNS_UD_CS_STRONG_ASYMMETRIC_KEYS : 0x0008,
|
||||
RNS_UD_CS_UNUSED : 0x0010,
|
||||
RNS_UD_CS_VALID_CONNECTION_TYPE : 0x0020,
|
||||
RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU : 0x0040,
|
||||
RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT : 0x0080,
|
||||
RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL : 0x0100,
|
||||
RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE : 0x0200,
|
||||
RNS_UD_CS_SUPPORT_HEARTBEAT_PDU : 0x0400
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var ConnectionType = {
|
||||
CONNECTION_TYPE_MODEM : 0x01,
|
||||
CONNECTION_TYPE_BROADBAND_LOW : 0x02,
|
||||
CONNECTION_TYPE_SATELLITE : 0x03,
|
||||
CONNECTION_TYPE_BROADBAND_HIGH : 0x04,
|
||||
CONNECTION_TYPE_WAN : 0x05,
|
||||
CONNECTION_TYPE_LAN : 0x06,
|
||||
CONNECTION_TYPE_AUTODETECT : 0x07
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
*/
|
||||
var VERSION = {
|
||||
RDP_VERSION_4 : 0x00080001,
|
||||
RDP_VERSION_5_PLUS : 0x00080004
|
||||
};
|
||||
|
||||
var Sequence = {
|
||||
RNS_UD_SAS_DEL : 0xAA03
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240511.aspx
|
||||
*/
|
||||
var EncryptionMethod = {
|
||||
ENCRYPTION_FLAG_40BIT : 0x00000001,
|
||||
ENCRYPTION_FLAG_128BIT : 0x00000002,
|
||||
ENCRYPTION_FLAG_56BIT : 0x00000008,
|
||||
FIPS_ENCRYPTION_FLAG : 0x00000010
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240518.aspx
|
||||
*/
|
||||
var EncryptionLevel = {
|
||||
ENCRYPTION_LEVEL_NONE : 0x00000000,
|
||||
ENCRYPTION_LEVEL_LOW : 0x00000001,
|
||||
ENCRYPTION_LEVEL_CLIENT_COMPATIBLE : 0x00000002,
|
||||
ENCRYPTION_LEVEL_HIGH : 0x00000003,
|
||||
ENCRYPTION_LEVEL_FIPS : 0x00000004
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240513.aspx
|
||||
*/
|
||||
var ChannelOptions = {
|
||||
CHANNEL_OPTION_INITIALIZED : 0x80000000,
|
||||
CHANNEL_OPTION_ENCRYPT_RDP : 0x40000000,
|
||||
CHANNEL_OPTION_ENCRYPT_SC : 0x20000000,
|
||||
CHANNEL_OPTION_ENCRYPT_CS : 0x10000000,
|
||||
CHANNEL_OPTION_PRI_HIGH : 0x08000000,
|
||||
CHANNEL_OPTION_PRI_MED : 0x04000000,
|
||||
CHANNEL_OPTION_PRI_LOW : 0x02000000,
|
||||
CHANNEL_OPTION_COMPRESS_RDP : 0x00800000,
|
||||
CHANNEL_OPTION_COMPRESS : 0x00400000,
|
||||
CHANNEL_OPTION_SHOW_PROTOCOL : 0x00200000,
|
||||
REMOTE_CONTROL_PERSISTENT : 0x00100000
|
||||
};
|
||||
|
||||
/**
|
||||
* IBM_101_102_KEYS is the most common keyboard type
|
||||
*/
|
||||
var KeyboardType = {
|
||||
IBM_PC_XT_83_KEY : 0x00000001,
|
||||
OLIVETTI : 0x00000002,
|
||||
IBM_PC_AT_84_KEY : 0x00000003,
|
||||
IBM_101_102_KEYS : 0x00000004,
|
||||
NOKIA_1050 : 0x00000005,
|
||||
NOKIA_9140 : 0x00000006,
|
||||
JAPANESE : 0x00000007
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://technet.microsoft.com/en-us/library/cc766503%28WS.10%29.aspx
|
||||
*/
|
||||
var KeyboardLayout = {
|
||||
ARABIC : 0x00000401,
|
||||
BULGARIAN : 0x00000402,
|
||||
CHINESE_US_KEYBOARD : 0x00000404,
|
||||
CZECH : 0x00000405,
|
||||
DANISH : 0x00000406,
|
||||
GERMAN : 0x00000407,
|
||||
GREEK : 0x00000408,
|
||||
US : 0x00000409,
|
||||
SPANISH : 0x0000040a,
|
||||
FINNISH : 0x0000040b,
|
||||
FRENCH : 0x0000040c,
|
||||
HEBREW : 0x0000040d,
|
||||
HUNGARIAN : 0x0000040e,
|
||||
ICELANDIC : 0x0000040f,
|
||||
ITALIAN : 0x00000410,
|
||||
JAPANESE : 0x00000411,
|
||||
KOREAN : 0x00000412,
|
||||
DUTCH : 0x00000413,
|
||||
NORWEGIAN : 0x00000414
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240521.aspx
|
||||
*/
|
||||
var CertificateType = {
|
||||
CERT_CHAIN_VERSION_1 : 0x00000001,
|
||||
CERT_CHAIN_VERSION_2 : 0x00000002
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {type.Type} data
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function block(data) {
|
||||
var self = {
|
||||
// type of data block
|
||||
type : new type.UInt16Le(function() {
|
||||
return self.data.obj.__TYPE__;
|
||||
}),
|
||||
// length of entire packet
|
||||
length : new type.UInt16Le(function() {
|
||||
return new type.Component(self).size();
|
||||
}),
|
||||
// data block
|
||||
data : data || new type.Factory(function(s){
|
||||
var options = {
|
||||
readLength : new type.CallableValue( function () {
|
||||
return self.length.value - 4;
|
||||
})
|
||||
};
|
||||
switch(self.type.value) {
|
||||
case MessageType.SC_CORE:
|
||||
self.data = serverCoreData(options).read(s);
|
||||
break;
|
||||
case MessageType.SC_SECURITY:
|
||||
self.data = serverSecurityData(options).read(s);
|
||||
break;
|
||||
case MessageType.SC_NET:
|
||||
self.data = serverNetworkData(null, options).read(s);
|
||||
break;
|
||||
case MessageType.CS_CORE:
|
||||
self.data = clientCoreData(options).read(s);
|
||||
break;
|
||||
case MessageType.CS_SECURITY:
|
||||
self.data = clientSecurityData(options).read(s);
|
||||
break;
|
||||
case MessageType.CS_NET:
|
||||
self.data = clientNetworkData(null, options).read(s);
|
||||
break;
|
||||
default:
|
||||
log.debug("unknown gcc block type " + self.type.value);
|
||||
self.data = new type.BinaryString(null, options).read(s);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main client informations
|
||||
* keyboard
|
||||
* screen definition
|
||||
* color depth
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientCoreData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.CS_CORE,
|
||||
rdpVersion : new type.UInt32Le(VERSION.RDP_VERSION_5_PLUS),
|
||||
desktopWidth : new type.UInt16Le(1280),
|
||||
desktopHeight : new type.UInt16Le(800),
|
||||
colorDepth : new type.UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP),
|
||||
sasSequence : new type.UInt16Le(Sequence.RNS_UD_SAS_DEL),
|
||||
kbdLayout : new type.UInt32Le(KeyboardLayout.FRENCH),
|
||||
clientBuild : new type.UInt32Le(3790),
|
||||
clientName : new type.BinaryString(Buffer.from('node-rdpjs\x00\x00\x00\x00\x00\x00', 'ucs2'), { readLength : new type.CallableValue(32) }),
|
||||
keyboardType : new type.UInt32Le(KeyboardType.IBM_101_102_KEYS),
|
||||
keyboardSubType : new type.UInt32Le(0),
|
||||
keyboardFnKeys : new type.UInt32Le(12),
|
||||
imeFileName : new type.BinaryString(Buffer.from(Array(64 + 1).join('\x00')), { readLength : new type.CallableValue(64), optional : true }),
|
||||
postBeta2ColorDepth : new type.UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP, { optional : true }),
|
||||
clientProductId : new type.UInt16Le(1, { optional : true }),
|
||||
serialNumber : new type.UInt32Le(0, { optional : true }),
|
||||
highColorDepth : new type.UInt16Le(HighColor.HIGH_COLOR_24BPP, { optional : true }),
|
||||
supportedColorDepths : new type.UInt16Le(Support.RNS_UD_15BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT | Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_32BPP_SUPPORT, { optional : true }),
|
||||
earlyCapabilityFlags : new type.UInt16Le(CapabilityFlag.RNS_UD_CS_SUPPORT_ERRINFO_PDU, { optional : true }),
|
||||
clientDigProductId : new type.BinaryString(Buffer.from(Array(64 + 1).join('\x00')), { optional : true, readLength : new type.CallableValue(64) }),
|
||||
connectionType : new type.UInt8(0, { optional : true }),
|
||||
pad1octet : new type.UInt8(0, { optional : true }),
|
||||
serverSelectedProtocol : new type.UInt32Le(0, { optional : true })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240517.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverCoreData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.SC_CORE,
|
||||
rdpVersion : new type.UInt32Le(VERSION.RDP_VERSION_5_PLUS),
|
||||
clientRequestedProtocol : new type.UInt32Le(null, { optional : true }),
|
||||
earlyCapabilityFlags : new type.UInt32Le(null, { optional : true })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240511.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientSecurityData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.CS_SECURITY,
|
||||
encryptionMethods : new type.UInt32Le(EncryptionMethod.ENCRYPTION_FLAG_40BIT | EncryptionMethod.ENCRYPTION_FLAG_56BIT | EncryptionMethod.ENCRYPTION_FLAG_128BIT),
|
||||
extEncryptionMethods : new type.UInt32Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only use for SSL (RDP security layer TODO)
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240518.aspx
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverSecurityData(opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.SC_SECURITY,
|
||||
encryptionMethod : new type.UInt32Le(),
|
||||
encryptionLevel : new type.UInt32Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel definition
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function channelDef (opt) {
|
||||
var self = {
|
||||
name : new type.BinaryString(null, { readLength : new type.CallableValue(8) }),
|
||||
options : new type.UInt32Le()
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional channel requests (sound, clipboard ...)
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function clientNetworkData(channelDefArray, opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.CS_NET,
|
||||
channelCount : new type.UInt32Le( function () {
|
||||
return self.channelDefArray.obj.length;
|
||||
}),
|
||||
channelDefArray : channelDefArray || new type.Factory( function (s) {
|
||||
self.channelDefArray = new type.Component([]);
|
||||
|
||||
for (var i = 0; i < self.channelCount.value; i++) {
|
||||
self.channelDefArray.obj.push(channelDef().read(s));
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param channelIds {type.Component} list of available channels
|
||||
* @param opt {object} Classic type options
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function serverNetworkData (channelIds, opt) {
|
||||
var self = {
|
||||
__TYPE__ : MessageType.SC_NET,
|
||||
MCSChannelId : new type.UInt16Le(1003, { constant : true }),
|
||||
channelCount : new type.UInt16Le(function () {
|
||||
return self.channelIdArray.obj.length;
|
||||
}),
|
||||
channelIdArray : channelIds || new type.Factory( function (s) {
|
||||
self.channelIdArray = new type.Component([]);
|
||||
for (var i = 0; i < self.channelCount.value; i++) {
|
||||
self.channelIdArray.obj.push(new type.UInt16Le().read(s));
|
||||
}
|
||||
}),
|
||||
pad : new type.UInt16Le(null, { conditional : function () {
|
||||
return (self.channelCount.value % 2) === 1;
|
||||
}})
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client or server GCC settings block
|
||||
* @param blocks {type.Component} array of gcc blocks
|
||||
* @param opt {object} options to component type
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function settings(blocks, opt) {
|
||||
var self = {
|
||||
blocks : blocks || new type.Factory(function(s) {
|
||||
self.blocks = new type.Component([]);
|
||||
// read until end of stream
|
||||
while(s.availableLength() > 0) {
|
||||
self.blocks.obj.push(block().read(s));
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read GCC response from server
|
||||
* @param s {type.Stream} current stream
|
||||
* @returns {Array(type.Component)} list of server block
|
||||
*/
|
||||
function readConferenceCreateResponse(s) {
|
||||
per.readChoice(s);
|
||||
|
||||
if(!per.readObjectIdentifier(s, t124_02_98_oid)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_OBJECT_IDENTIFIER_T124');
|
||||
}
|
||||
|
||||
per.readLength(s);
|
||||
per.readChoice(s);
|
||||
per.readInteger16(s, 1001);
|
||||
per.readInteger(s);
|
||||
per.readEnumerates(s);
|
||||
per.readNumberOfSet(s);
|
||||
per.readChoice(s);
|
||||
|
||||
if (!per.readOctetStream(s, h221_sc_key, 4)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY');
|
||||
}
|
||||
|
||||
length = per.readLength(s);
|
||||
serverSettings = settings(null, { readLength : new type.CallableValue(length) });
|
||||
|
||||
// Object magic
|
||||
return serverSettings.read(s).obj.blocks.obj.map(function(e) {
|
||||
return e.obj.data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Read GCC request
|
||||
* @param s {type.Stream}
|
||||
* @returns {Array(type.Component)} list of client block
|
||||
*/
|
||||
function readConferenceCreateRequest (s) {
|
||||
per.readChoice(s);
|
||||
if (!per.readObjectIdentifier(s, t124_02_98_oid)) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY');
|
||||
}
|
||||
per.readLength(s);
|
||||
per.readChoice(s);
|
||||
per.readSelection(s);
|
||||
per.readNumericString(s, 1);
|
||||
per.readPadding(s, 1);
|
||||
|
||||
if (per.readNumberOfSet(s) !== 1) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_SET');
|
||||
}
|
||||
|
||||
if (per.readChoice(s) !== 0xc0) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_T125_GCC_BAD_CHOICE');
|
||||
}
|
||||
|
||||
per.readOctetStream(s, h221_cs_key, 4);
|
||||
|
||||
length = per.readLength(s);
|
||||
var clientSettings = settings(null, { readLength : new type.CallableValue(length) });
|
||||
|
||||
// Object magic
|
||||
return clientSettings.read(s).obj.blocks.obj.map(function(e) {
|
||||
return e.obj.data;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Built {type.Componen} from gcc user data
|
||||
* @param userData {type.Component} GCC data from client
|
||||
* @returns {type.Component} GCC encoded client user data
|
||||
*/
|
||||
function writeConferenceCreateRequest (userData) {
|
||||
var userDataStream = new type.Stream(userData.size());
|
||||
userData.write(userDataStream);
|
||||
|
||||
return new type.Component([
|
||||
per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
||||
per.writeLength(userData.size() + 14), per.writeChoice(0),
|
||||
per.writeSelection(0x08), per.writeNumericString("1", 1), per.writePadding(1),
|
||||
per.writeNumberOfSet(1), per.writeChoice(0xc0),
|
||||
per.writeOctetStream(Buffer.from(h221_cs_key), 4), per.writeOctetStream(userDataStream.getValue())
|
||||
]);
|
||||
}
|
||||
|
||||
function writeConferenceCreateResponse (userData) {
|
||||
var userDataStream = new type.Stream(userData.size());
|
||||
userData.write(userDataStream);
|
||||
|
||||
return new type.Component([
|
||||
per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
||||
per.writeLength(userData.size() + 14), per.writeChoice(0x14),
|
||||
per.writeInteger16(0x79F3, 1001), per.writeInteger(1), per.writeEnumerates(0),
|
||||
per.writeNumberOfSet(1), per.writeChoice(0xc0),
|
||||
per.writeOctetStream(Buffer.from(h221_sc_key), 4), per.writeOctetStream(userDataStream.getValue())
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
MessageType : MessageType,
|
||||
VERSION : VERSION,
|
||||
KeyboardLayout : KeyboardLayout,
|
||||
block : block,
|
||||
clientCoreData : clientCoreData,
|
||||
clientNetworkData : clientNetworkData,
|
||||
clientSecurityData : clientSecurityData,
|
||||
serverCoreData : serverCoreData,
|
||||
serverSecurityData : serverSecurityData,
|
||||
serverNetworkData : serverNetworkData,
|
||||
readConferenceCreateResponse : readConferenceCreateResponse,
|
||||
readConferenceCreateRequest : readConferenceCreateRequest,
|
||||
writeConferenceCreateRequest : writeConferenceCreateRequest,
|
||||
writeConferenceCreateResponse : writeConferenceCreateResponse
|
||||
};
|
||||
26
rdp/protocol/t125/index.js
Normal file
26
rdp/protocol/t125/index.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var mcs = require('./mcs');
|
||||
var gcc = require('./gcc');
|
||||
|
||||
module.exports = {
|
||||
mcs : mcs,
|
||||
gcc : gcc
|
||||
};
|
||||
490
rdp/protocol/t125/mcs.js
Normal file
490
rdp/protocol/t125/mcs.js
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var events = require('events');
|
||||
var type = require('../../core').type;
|
||||
var log = require('../../core').log;
|
||||
var error = require('../../core').error;
|
||||
var gcc = require('./gcc');
|
||||
var per = require('./per');
|
||||
var asn1 = require('../../asn1');
|
||||
|
||||
var Message = {
|
||||
MCS_TYPE_CONNECT_INITIAL : 0x65,
|
||||
MCS_TYPE_CONNECT_RESPONSE : 0x66
|
||||
};
|
||||
|
||||
var DomainMCSPDU = {
|
||||
ERECT_DOMAIN_REQUEST : 1,
|
||||
DISCONNECT_PROVIDER_ULTIMATUM : 8,
|
||||
ATTACH_USER_REQUEST : 10,
|
||||
ATTACH_USER_CONFIRM : 11,
|
||||
CHANNEL_JOIN_REQUEST : 14,
|
||||
CHANNEL_JOIN_CONFIRM : 15,
|
||||
SEND_DATA_REQUEST : 25,
|
||||
SEND_DATA_INDICATION : 26
|
||||
};
|
||||
|
||||
var Channel = {
|
||||
MCS_GLOBAL_CHANNEL : 1003,
|
||||
MCS_USERCHANNEL_BASE : 1001
|
||||
};
|
||||
|
||||
/**
|
||||
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function DomainParameters(maxChannelIds, maxUserIds, maxTokenIds,
|
||||
numPriorities, minThoughput, maxHeight, maxMCSPDUsize, protocolVersion) {
|
||||
return new asn1.univ.Sequence({
|
||||
maxChannelIds : new asn1.univ.Integer(maxChannelIds),
|
||||
maxUserIds : new asn1.univ.Integer(maxUserIds),
|
||||
maxTokenIds : new asn1.univ.Integer(maxTokenIds),
|
||||
numPriorities : new asn1.univ.Integer(numPriorities),
|
||||
minThoughput : new asn1.univ.Integer(minThoughput),
|
||||
maxHeight : new asn1.univ.Integer(maxHeight),
|
||||
maxMCSPDUsize : new asn1.univ.Integer(maxMCSPDUsize),
|
||||
protocolVersion : new asn1.univ.Integer(protocolVersion)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
||||
* @param userData {Buffer}
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function ConnectInitial (userData) {
|
||||
return new asn1.univ.Sequence({
|
||||
callingDomainSelector : new asn1.univ.OctetString(Buffer.from('\x01', 'binary')),
|
||||
calledDomainSelector : new asn1.univ.OctetString(Buffer.from('\x01', 'binary')),
|
||||
upwardFlag : new asn1.univ.Boolean(true),
|
||||
targetParameters : DomainParameters(34, 2, 0, 1, 0, 1, 0xffff, 2),
|
||||
minimumParameters : DomainParameters(1, 1, 1, 1, 0, 1, 0x420, 2),
|
||||
maximumParameters : DomainParameters(0xffff, 0xfc17, 0xffff, 1, 0, 1, 0xffff, 2),
|
||||
userData : new asn1.univ.OctetString(userData)
|
||||
}).implicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Application, asn1.spec.TagFormat.Constructed, 101));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25
|
||||
* @returns {asn1.univ.Sequence}
|
||||
*/
|
||||
function ConnectResponse (userData) {
|
||||
return new asn1.univ.Sequence({
|
||||
result : new asn1.univ.Enumerate(0),
|
||||
calledConnectId : new asn1.univ.Integer(0),
|
||||
domainParameters : DomainParameters(22, 3, 0, 1, 0, 1,0xfff8, 2),
|
||||
userData : new asn1.univ.OctetString(userData)
|
||||
}).implicitTag(new asn1.spec.Asn1Tag(asn1.spec.TagClass.Application, asn1.spec.TagFormat.Constructed, 102));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format MCS PDU header packet
|
||||
* @param mcsPdu {integer}
|
||||
* @param options {integer}
|
||||
* @returns {type.UInt8} headers
|
||||
*/
|
||||
function writeMCSPDUHeader(mcsPdu, options) {
|
||||
options = options || 0;
|
||||
return new type.UInt8((mcsPdu << 2) | options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read MCS PDU header
|
||||
* @param opcode
|
||||
* @param mcsPdu
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function readMCSPDUHeader(opcode, mcsPdu) {
|
||||
return (opcode >> 2) === mcsPdu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-Channel Services
|
||||
* @param transport {events.EventEmitter} transport layer listen (connect, data) events
|
||||
* @param recvOpCode {DomainMCSPDU} opcode use in receive automata
|
||||
* @param sendOpCode {DomainMCSPDU} opcode use to send message
|
||||
*/
|
||||
function MCS(transport, recvOpCode, sendOpCode) {
|
||||
this.transport = transport;
|
||||
this.recvOpCode = recvOpCode;
|
||||
this.sendOpCode = sendOpCode;
|
||||
this.channels = [{id : Channel.MCS_GLOBAL_CHANNEL, name : 'global'}];
|
||||
this.channels.find = function(callback) {
|
||||
for(var i in this) {
|
||||
if(callback(this[i])) return this[i];
|
||||
};
|
||||
};
|
||||
|
||||
// bind events
|
||||
var self = this;
|
||||
this.transport.on('close', function () {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
//inherit from Layer
|
||||
inherits(MCS, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Send message to a specific channel
|
||||
* @param channelName {string} name of channel
|
||||
* @param data {type.*} message to send
|
||||
*/
|
||||
MCS.prototype.send = function(channelName, data) {
|
||||
var channelId = this.channels.find(function(element) {
|
||||
if (element.name === channelName) return true;
|
||||
}).id;
|
||||
|
||||
this.transport.send(new type.Component([
|
||||
writeMCSPDUHeader(this.sendOpCode),
|
||||
per.writeInteger16(this.userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId),
|
||||
new type.UInt8(0x70),
|
||||
per.writeLength(data.size()),
|
||||
data
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Main receive function
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
MCS.prototype.recv = function(s) {
|
||||
opcode = new type.UInt8().read(s).value;
|
||||
|
||||
if (readMCSPDUHeader(opcode, DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM)) {
|
||||
log.info("MCS DISCONNECT_PROVIDER_ULTIMATUM");
|
||||
this.transport.close();
|
||||
return
|
||||
}
|
||||
else if(!readMCSPDUHeader(opcode, this.recvOpCode)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_BAD_RECEIVE_OPCODE');
|
||||
}
|
||||
|
||||
per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
|
||||
|
||||
var channelId = per.readInteger16(s);
|
||||
|
||||
per.readEnumerates(s);
|
||||
per.readLength(s);
|
||||
|
||||
var channelName = this.channels.find(function(e) {
|
||||
if (e.id === channelId) return true;
|
||||
}).name;
|
||||
|
||||
this.emit(channelName, s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Only main channels handle actually
|
||||
* @param transport {event.EventEmitter} bind connect and data events
|
||||
* @returns
|
||||
*/
|
||||
function Client(transport) {
|
||||
MCS.call(this, transport, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST);
|
||||
|
||||
// channel context automata
|
||||
this.channelsConnected = 0;
|
||||
|
||||
// init gcc information
|
||||
this.clientCoreData = gcc.clientCoreData();
|
||||
this.clientNetworkData = gcc.clientNetworkData(new type.Component([]));
|
||||
this.clientSecurityData = gcc.clientSecurityData();
|
||||
|
||||
// must be readed from protocol
|
||||
this.serverCoreData = null;
|
||||
this.serverSecurityData = null;
|
||||
this.serverNetworkData = null;
|
||||
|
||||
var self = this;
|
||||
this.transport.on('connect', function(s) {
|
||||
self.connect(s);
|
||||
});
|
||||
}
|
||||
|
||||
inherits(Client, MCS);
|
||||
|
||||
/**
|
||||
* Connect event layer
|
||||
*/
|
||||
Client.prototype.connect = function(selectedProtocol) {
|
||||
this.clientCoreData.obj.serverSelectedProtocol.value = selectedProtocol;
|
||||
this.sendConnectInitial();
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connect response (server->client)
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvConnectResponse = function(s) {
|
||||
var userData = new type.Stream(ConnectResponse().decode(s, asn1.ber).value.userData.value);
|
||||
var serverSettings = gcc.readConferenceCreateResponse(userData);
|
||||
// record server gcc block
|
||||
for(var i in serverSettings) {
|
||||
if(!serverSettings[i].obj) {
|
||||
continue;
|
||||
}
|
||||
switch(serverSettings[i].obj.__TYPE__) {
|
||||
case gcc.MessageType.SC_CORE:
|
||||
this.serverCoreData = serverSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.SC_SECURITY:
|
||||
this.serverSecurityData = serverSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.SC_NET:
|
||||
this.serverNetworkData = serverSettings[i];
|
||||
break;
|
||||
default:
|
||||
log.warn('unhandle server gcc block : ' + serverSettings[i].obj.__TYPE__);
|
||||
}
|
||||
}
|
||||
|
||||
// send domain request
|
||||
this.sendErectDomainRequest();
|
||||
// send attach user request
|
||||
this.sendAttachUserRequest();
|
||||
// now wait user confirm from server
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvAttachUserConfirm(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connection automata step
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvAttachUserConfirm = function(s) {
|
||||
if (!readMCSPDUHeader(new type.UInt8().read(s).value, DomainMCSPDU.ATTACH_USER_CONFIRM)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER');
|
||||
}
|
||||
|
||||
if (per.readEnumerates(s) !== 0) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_REJECT_USER');
|
||||
}
|
||||
|
||||
this.userId = per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
|
||||
//ask channel for specific user
|
||||
this.channels.push({ id : this.userId, name : 'user' });
|
||||
// channel connect automata
|
||||
this.connectChannels();
|
||||
};
|
||||
|
||||
/**
|
||||
* Last state in channel connection automata
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.recvChannelJoinConfirm = function(s) {
|
||||
var opcode = new type.UInt8().read(s).value;
|
||||
|
||||
if (!readMCSPDUHeader(opcode, DomainMCSPDU.CHANNEL_JOIN_CONFIRM)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_WAIT_CHANNEL_JOIN_CONFIRM');
|
||||
}
|
||||
|
||||
var confirm = per.readEnumerates(s);
|
||||
|
||||
var userId = per.readInteger16(s, Channel.MCS_USERCHANNEL_BASE);
|
||||
if (this.userId !== userId) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_INVALID_USER_ID');
|
||||
}
|
||||
|
||||
var channelId = per.readInteger16(s);
|
||||
|
||||
if ((confirm !== 0) && (channelId === Channel.MCS_GLOBAL_CHANNEL || channelId === this.userId)) {
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL');
|
||||
}
|
||||
|
||||
this.connectChannels();
|
||||
};
|
||||
|
||||
/**
|
||||
* First MCS message
|
||||
*/
|
||||
Client.prototype.sendConnectInitial = function() {
|
||||
|
||||
var ccReq = gcc.writeConferenceCreateRequest(new type.Component([
|
||||
gcc.block(this.clientCoreData),
|
||||
gcc.block(this.clientNetworkData),
|
||||
gcc.block(this.clientSecurityData)
|
||||
])).toStream().getValue();
|
||||
|
||||
this.transport.send(ConnectInitial(ccReq).encode(asn1.ber));
|
||||
|
||||
// next event is connect response
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvConnectResponse(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connection automata step
|
||||
*/
|
||||
Client.prototype.sendErectDomainRequest = function() {
|
||||
this.transport.send(new type.Component([
|
||||
writeMCSPDUHeader(DomainMCSPDU.ERECT_DOMAIN_REQUEST),
|
||||
per.writeInteger(0),
|
||||
per.writeInteger(0)
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* MCS connection automata step
|
||||
*/
|
||||
Client.prototype.sendAttachUserRequest = function() {
|
||||
this.transport.send(writeMCSPDUHeader(DomainMCSPDU.ATTACH_USER_REQUEST));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a channel join request
|
||||
* @param channelId {integer} channel id
|
||||
*/
|
||||
Client.prototype.sendChannelJoinRequest = function(channelId) {
|
||||
this.transport.send(new type.Component([
|
||||
writeMCSPDUHeader(DomainMCSPDU.CHANNEL_JOIN_REQUEST),
|
||||
per.writeInteger16(this.userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId)
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect channels automata
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Client.prototype.connectChannels = function(s) {
|
||||
if(this.channelsConnected == this.channels.length) {
|
||||
var self = this;
|
||||
this.transport.on('data', function(s) {
|
||||
self.recv(s);
|
||||
});
|
||||
|
||||
// send client and sever gcc informations
|
||||
this.emit('connect',
|
||||
{
|
||||
core : this.clientCoreData.obj,
|
||||
security : this.clientSecurityData.obj,
|
||||
net : this.clientNetworkData.obj
|
||||
},
|
||||
{
|
||||
core : this.serverCoreData.obj,
|
||||
security : this.serverSecurityData.obj
|
||||
}, this.userId, this.channels);
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendChannelJoinRequest(this.channels[this.channelsConnected++].id);
|
||||
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvChannelJoinConfirm(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Server side of MCS layer
|
||||
* @param transport
|
||||
*/
|
||||
function Server (transport) {
|
||||
MCS.call(this, transport, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION);
|
||||
|
||||
// must be readed from protocol
|
||||
this.clientCoreData = null;
|
||||
this.clientNetworkData = null;
|
||||
this.clientSecurityData = null;
|
||||
|
||||
// init gcc information
|
||||
this.serverCoreData = gcc.serverCoreData();
|
||||
this.serverSecurityData = gcc.serverSecurityData();
|
||||
this.serverNetworkData = gcc.serverNetworkData(new type.Component([]));
|
||||
|
||||
var self = this;
|
||||
this.transport.on('connect', function (selectedProtocol) {
|
||||
self.serverCoreData.obj.clientRequestedProtocol.value = selectedProtocol;
|
||||
}).once('data', function (s) {
|
||||
self.recvConnectInitial(s);
|
||||
});
|
||||
}
|
||||
|
||||
inherits(Server, MCS);
|
||||
|
||||
/**
|
||||
* First state of server automata
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Server.prototype.recvConnectInitial = function (s) {
|
||||
var userData = new type.Stream(ConnectInitial().decode(s, asn1.ber).value.userData.value);
|
||||
var clientSettings = gcc.readConferenceCreateRequest(userData);
|
||||
// record server gcc block
|
||||
for(var i in clientSettings) {
|
||||
if(!clientSettings[i].obj) {
|
||||
continue;
|
||||
}
|
||||
switch(clientSettings[i].obj.__TYPE__) {
|
||||
case gcc.MessageType.CS_CORE:
|
||||
this.clientCoreData = clientSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.CS_SECURITY:
|
||||
this.clientSecurityData = clientSettings[i];
|
||||
break;
|
||||
case gcc.MessageType.CS_NET:
|
||||
this.clientNetworkData = clientSettings[i];
|
||||
for (var i = 0; i < this.clientNetworkData.obj.channelCount.value; i++) {
|
||||
this.serverNetworkData.obj.channelIdArray.obj.push(new type.UInt16Le( i + 1 + Channel.MCS_GLOBAL_CHANNEL));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log.debug('unhandle client gcc block : ' + clientSettings[i].obj.__TYPE__);
|
||||
}
|
||||
}
|
||||
|
||||
this.sendConnectResponse();
|
||||
};
|
||||
|
||||
/**
|
||||
* State 2 in mcs server connetion automata
|
||||
*/
|
||||
Server.prototype.sendConnectResponse = function () {
|
||||
var ccReq = gcc.writeConferenceCreateResponse(new type.Component([
|
||||
gcc.block(this.serverCoreData),
|
||||
gcc.block(this.serverSecurityData),
|
||||
gcc.block(this.serverNetworkData),
|
||||
])).toStream().getValue();
|
||||
|
||||
this.transport.send(ConnectResponse(ccReq).encode(asn1.ber));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
Client : Client,
|
||||
Server : Server
|
||||
};
|
||||
338
rdp/protocol/t125/per.js
Normal file
338
rdp/protocol/t125/per.js
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var type = require('../../core').type;
|
||||
var error = require('../../core').error;
|
||||
|
||||
/**
|
||||
* @param s {type.Stream} read value from stream
|
||||
* @returns read length from per format
|
||||
*/
|
||||
function readLength(s) {
|
||||
var byte = new type.UInt8().read(s).value;
|
||||
var size = 0;
|
||||
if(byte & 0x80) {
|
||||
byte &= ~0x80;
|
||||
size = byte << 8;
|
||||
size += new type.UInt8().read(s).value;
|
||||
}
|
||||
else {
|
||||
size = byte;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value {raw} value to convert to per format
|
||||
* @returns type objects per encoding value
|
||||
*/
|
||||
function writeLength(value) {
|
||||
if(value > 0x7f) {
|
||||
return new type.UInt16Be(value | 0x8000);
|
||||
}
|
||||
else {
|
||||
return new type.UInt8(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} choice decoding from per encoding
|
||||
*/
|
||||
function readChoice(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param choice {integer}
|
||||
* @returns {type.UInt8} choice per encoded
|
||||
*/
|
||||
function writeChoice(choice) {
|
||||
return new type.UInt8(choice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} number represent selection
|
||||
*/
|
||||
function readSelection(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selection {integer}
|
||||
* @returns {type.UInt8} per encoded selection
|
||||
*/
|
||||
function writeSelection(selection) {
|
||||
return new type.UInt8(selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} number of sets
|
||||
*/
|
||||
function readNumberOfSet(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numberOfSet {integer}
|
||||
* @returns {type.UInt8} per encoded nuimber of sets
|
||||
*/
|
||||
function writeNumberOfSet(numberOfSet) {
|
||||
return new type.UInt8(numberOfSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} enumerates number
|
||||
*/
|
||||
function readEnumerates(s) {
|
||||
return new type.UInt8().read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param enumerate {integer}
|
||||
* @returns {type.UInt8} per encoded enumerate
|
||||
*/
|
||||
function writeEnumerates(enumerate) {
|
||||
return new type.UInt8(enumerate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @returns {integer} integer per decoded
|
||||
*/
|
||||
function readInteger(s) {
|
||||
var result;
|
||||
var size = readLength(s);
|
||||
switch(size) {
|
||||
case 1:
|
||||
result = new type.UInt8();
|
||||
break;
|
||||
case 2:
|
||||
result = new type.UInt16Be();
|
||||
break;
|
||||
case 4:
|
||||
result = new type.UInt32Be();
|
||||
break;
|
||||
default:
|
||||
throw new error.UnexpectedFatalError('NODE_RDP_PROTOCOL_T125_PER_BAD_INTEGER_LENGTH');
|
||||
}
|
||||
return result.read(s).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value {integer}
|
||||
* @returns {type.Component} per encoded integer
|
||||
*/
|
||||
function writeInteger(value) {
|
||||
if(value <= 0xff) {
|
||||
return new type.Component([writeLength(1), new type.UInt8(value)]);
|
||||
}
|
||||
else if(value < 0xffff) {
|
||||
return new type.Component([writeLength(2), new type.UInt16Be(value)]);
|
||||
}
|
||||
else {
|
||||
return new type.Component([writeLength(4), new type.UInt32Be(value)]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param minimum {integer} increment (default 0)
|
||||
* @returns {integer} per decoded integer 16 bits
|
||||
*/
|
||||
function readInteger16(s, minimum) {
|
||||
return new type.UInt16Be().read(s).value + (minimum || 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value {integer}
|
||||
* @param minimum {integer} decrement (default 0)
|
||||
* @returns {type.UInt16Be} per encoded integer 16 bits
|
||||
*/
|
||||
function writeInteger16(value, minimum) {
|
||||
return new type.UInt16Be(value - (minimum || 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check object identifier
|
||||
* @param s {type.Stream}
|
||||
* @param oid {array} object identifier to check
|
||||
*/
|
||||
function readObjectIdentifier(s, oid) {
|
||||
var size = readLength(s);
|
||||
if(size !== 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var a_oid = [0, 0, 0, 0, 0, 0];
|
||||
var t12 = new type.UInt8().read(s).value;
|
||||
a_oid[0] = t12 >> 4;
|
||||
a_oid[1] = t12 & 0x0f;
|
||||
a_oid[2] = new type.UInt8().read(s).value;
|
||||
a_oid[3] = new type.UInt8().read(s).value;
|
||||
a_oid[4] = new type.UInt8().read(s).value;
|
||||
a_oid[5] = new type.UInt8().read(s).value;
|
||||
|
||||
for(var i in oid) {
|
||||
if(oid[i] !== a_oid[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oid {array} oid to write
|
||||
* @returns {type.Component} per encoded object identifier
|
||||
*/
|
||||
function writeObjectIdentifier(oid) {
|
||||
return new type.Component([new type.UInt8(5), new type.UInt8((oid[0] << 4) & (oid[1] & 0x0f)), new type.UInt8(oid[2]), new type.UInt8(oid[3]), new type.UInt8(oid[4]), new type.UInt8(oid[5])]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read as padding...
|
||||
* @param s {type.Stream}
|
||||
* @param minValue
|
||||
*/
|
||||
function readNumericString(s, minValue) {
|
||||
var length = readLength(s);
|
||||
length = (length + minValue + 1) / 2;
|
||||
s.readPadding(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nStr {String}
|
||||
* @param minValue {integer}
|
||||
* @returns {type.Component} per encoded numeric string
|
||||
*/
|
||||
function writeNumericString(nStr, minValue) {
|
||||
var length = nStr.length;
|
||||
var mlength = minValue;
|
||||
if(length - minValue >= 0) {
|
||||
mlength = length - minValue;
|
||||
}
|
||||
|
||||
var result = [];
|
||||
|
||||
for(var i = 0; i < length; i += 2) {
|
||||
var c1 = nStr.charCodeAt(i);
|
||||
var c2 = 0;
|
||||
if(i + 1 < length) {
|
||||
c2 = nStr.charCodeAt(i + 1);
|
||||
}
|
||||
else {
|
||||
c2 = 0x30;
|
||||
}
|
||||
c1 = (c1 - 0x30) % 10;
|
||||
c2 = (c2 - 0x30) % 10;
|
||||
|
||||
result[result.length] = new type.UInt8((c1 << 4) | c2);
|
||||
}
|
||||
|
||||
return new type.Component([writeLength(mlength), new type.Component(result)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param length {integer} length of padding
|
||||
*/
|
||||
function readPadding(s, length) {
|
||||
s.readPadding(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param length {integer} length of padding
|
||||
* @returns {type.BinaryString} per encoded padding
|
||||
*/
|
||||
function writePadding(length) {
|
||||
return new type.BinaryString(Buffer.from(Array(length + 1).join("\x00")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s {type.Stream}
|
||||
* @param octetStream {String}
|
||||
* @param minValue {integer} default 0
|
||||
* @returns {Boolean} true if read octectStream is equal to octetStream
|
||||
*/
|
||||
function readOctetStream(s, octetStream, minValue) {
|
||||
var size = readLength(s) + (minValue || 0);
|
||||
if(size !== octetStream.length) {
|
||||
return false;
|
||||
}
|
||||
for(var i = 0; i < size; i++) {
|
||||
var c = new type.UInt8().read(s);
|
||||
if(octetStream.charCodeAt(i) !== c.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param oStr {String}
|
||||
* @param minValue {integer} default 0
|
||||
* @returns {type.Component} per encoded octet stream
|
||||
*/
|
||||
function writeOctetStream(oStr, minValue) {
|
||||
minValue = minValue || 0;
|
||||
var length = oStr.length;
|
||||
var mlength = minValue;
|
||||
|
||||
if(length - minValue >= 0) {
|
||||
mlength = length - minValue;
|
||||
}
|
||||
|
||||
result = [];
|
||||
for(var i = 0; i < length; i++) {
|
||||
result[result.length] = new type.UInt8(oStr[i]);
|
||||
}
|
||||
|
||||
return new type.Component([writeLength(mlength), new type.Component(result)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
readLength : readLength,
|
||||
writeLength : writeLength,
|
||||
readChoice : readChoice,
|
||||
writeChoice : writeChoice,
|
||||
readSelection : readSelection,
|
||||
writeSelection : writeSelection,
|
||||
readNumberOfSet : readNumberOfSet,
|
||||
writeNumberOfSet : writeNumberOfSet,
|
||||
readEnumerates : readEnumerates,
|
||||
writeEnumerates : writeEnumerates,
|
||||
readInteger : readInteger,
|
||||
writeInteger : writeInteger,
|
||||
readInteger16 : readInteger16,
|
||||
writeInteger16 : writeInteger16,
|
||||
readObjectIdentifier : readObjectIdentifier,
|
||||
writeObjectIdentifier : writeObjectIdentifier,
|
||||
readNumericString : readNumericString,
|
||||
writeNumericString : writeNumericString,
|
||||
readPadding : readPadding,
|
||||
writePadding : writePadding,
|
||||
readOctetStream : readOctetStream,
|
||||
writeOctetStream : writeOctetStream
|
||||
};
|
||||
171
rdp/protocol/tpkt.js
Normal file
171
rdp/protocol/tpkt.js
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var type = require('../core').type;
|
||||
var events = require('events');
|
||||
|
||||
/**
|
||||
* Type of tpkt packet
|
||||
* Fastpath is use to shortcut RDP stack
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240621.aspx
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240589.aspx
|
||||
*/
|
||||
var Action = {
|
||||
FASTPATH_ACTION_FASTPATH : 0x0,
|
||||
FASTPATH_ACTION_X224 : 0x3
|
||||
};
|
||||
|
||||
/**
|
||||
* TPKT layer of rdp stack
|
||||
*/
|
||||
function TPKT(transport) {
|
||||
this.transport = transport;
|
||||
// wait 2 bytes
|
||||
this.transport.expect(2);
|
||||
// next state is receive header
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvHeader(s);
|
||||
}).on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* inherit from a packet layer
|
||||
*/
|
||||
inherits(TPKT, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Receive correct packet as expected
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
TPKT.prototype.recvHeader = function (s) {
|
||||
var version = new type.UInt8().read(s).value;
|
||||
var self = this;
|
||||
if(version === Action.FASTPATH_ACTION_X224) {
|
||||
new type.UInt8().read(s);
|
||||
this.transport.expect(2);
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvExtendedHeader(s);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.secFlag = ((version >> 6) & 0x3);
|
||||
var length = new type.UInt8().read(s).value;
|
||||
if (length & 0x80) {
|
||||
this.transport.expect(1);
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvExtendedFastPathHeader(s, length);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.transport.expect(length - 2);
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvFastPath(s);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive second part of header packet
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
TPKT.prototype.recvExtendedHeader = function (s) {
|
||||
var size = new type.UInt16Be().read(s);
|
||||
this.transport.expect(size.value - 4);
|
||||
//next state receive packet
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvData(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive data available for presentation layer
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
TPKT.prototype.recvData = function (s) {
|
||||
this.emit('data', s);
|
||||
this.transport.expect(2);
|
||||
//next state receive header
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvHeader(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Read extended fastpath header
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
TPKT.prototype.recvExtendedFastPathHeader = function (s, length) {
|
||||
var rightPart = new type.UInt8().read(s).value;
|
||||
var leftPart = length & ~0x80;
|
||||
var packetSize = (leftPart << 8) + rightPart;
|
||||
|
||||
var self = this;
|
||||
this.transport.expect(packetSize - 3);
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvFastPath(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Read fast path data
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
TPKT.prototype.recvFastPath = function (s) {
|
||||
this.emit('fastPathData', this.secFlag, s);
|
||||
var self = this;
|
||||
this.transport.expect(2);
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvHeader(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Send message throught TPKT layer
|
||||
* @param message {type.*}
|
||||
*/
|
||||
TPKT.prototype.send = function(message) {
|
||||
this.transport.send(new type.Component([
|
||||
new type.UInt8(Action.FASTPATH_ACTION_X224),
|
||||
new type.UInt8(0),
|
||||
new type.UInt16Be(message.size() + 4),
|
||||
message
|
||||
]));
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
TPKT.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = TPKT;
|
||||
|
||||
346
rdp/protocol/x224.js
Normal file
346
rdp/protocol/x224.js
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
*
|
||||
* This file is part of node-rdpjs.
|
||||
*
|
||||
* node-rdpjs is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var inherits = require('util').inherits;
|
||||
var events = require('events');
|
||||
var type = require('../core').type;
|
||||
var log = require('../core').log;
|
||||
var error = require('../core').error;
|
||||
|
||||
/**
|
||||
* Message type present in X224 packet header
|
||||
*/
|
||||
var MessageType = {
|
||||
X224_TPDU_CONNECTION_REQUEST : 0xE0,
|
||||
X224_TPDU_CONNECTION_CONFIRM : 0xD0,
|
||||
X224_TPDU_DISCONNECT_REQUEST : 0x80,
|
||||
X224_TPDU_DATA : 0xF0,
|
||||
X224_TPDU_ERROR : 0x70
|
||||
};
|
||||
|
||||
/**
|
||||
* Type of negotiation present in negotiation packet
|
||||
*/
|
||||
var NegotiationType = {
|
||||
TYPE_RDP_NEG_REQ : 0x01,
|
||||
TYPE_RDP_NEG_RSP : 0x02,
|
||||
TYPE_RDP_NEG_FAILURE : 0x03
|
||||
};
|
||||
|
||||
/**
|
||||
* Protocols available for x224 layer
|
||||
*/
|
||||
var Protocols = {
|
||||
PROTOCOL_RDP : 0x00000000,
|
||||
PROTOCOL_SSL : 0x00000001,
|
||||
PROTOCOL_HYBRID : 0x00000002,
|
||||
PROTOCOL_HYBRID_EX : 0x00000008
|
||||
};
|
||||
|
||||
/**
|
||||
* Use to negotiate security layer of RDP stack
|
||||
* In node-rdpjs only ssl is available
|
||||
* @param opt {object} component type options
|
||||
* @see request -> http://msdn.microsoft.com/en-us/library/cc240500.aspx
|
||||
* @see response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx
|
||||
* @see failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx
|
||||
*/
|
||||
function negotiation(opt) {
|
||||
var self = {
|
||||
type : new type.UInt8(),
|
||||
flag : new type.UInt8(),
|
||||
length : new type.UInt16Le(0x0008, { constant : true }),
|
||||
result : new type.UInt32Le()
|
||||
};
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* X224 client connection request
|
||||
* @param opt {object} component type options
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240470.aspx
|
||||
*/
|
||||
function clientConnectionRequestPDU(opt, cookie) {
|
||||
var self = {
|
||||
len : new type.UInt8(function() {
|
||||
return new type.Component(self).size() - 1;
|
||||
}),
|
||||
code : new type.UInt8(MessageType.X224_TPDU_CONNECTION_REQUEST, { constant : true }),
|
||||
padding : new type.Component([new type.UInt16Le(), new type.UInt16Le(), new type.UInt8()]),
|
||||
cookie : cookie || new type.Factory( function (s) {
|
||||
var offset = 0;
|
||||
while (true) {
|
||||
var token = s.buffer.readUInt16LE(s.offset + offset);
|
||||
if (token === 0x0a0d) {
|
||||
self.cookie = new type.BinaryString(null, { readLength : new type.CallableValue(offset + 2) }).read(s);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
}, { conditional : function () {
|
||||
return self.len.value > 14;
|
||||
}}),
|
||||
protocolNeg : negotiation({ optional : true })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* X224 Server connection confirm
|
||||
* @param opt {object} component type options
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240506.aspx
|
||||
*/
|
||||
function serverConnectionConfirm(opt) {
|
||||
var self = {
|
||||
len : new type.UInt8(function() {
|
||||
return new type.Component(self).size() - 1;
|
||||
}),
|
||||
code : new type.UInt8(MessageType.X224_TPDU_CONNECTION_CONFIRM, { constant : true }),
|
||||
padding : new type.Component([new type.UInt16Le(), new type.UInt16Le(), new type.UInt8()]),
|
||||
protocolNeg : negotiation({ optional : true })
|
||||
};
|
||||
|
||||
return new type.Component(self, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Header of each data message from x224 layer
|
||||
* @returns {type.Component}
|
||||
*/
|
||||
function x224DataHeader() {
|
||||
var self = {
|
||||
header : new type.UInt8(2),
|
||||
messageType : new type.UInt8(MessageType.X224_TPDU_DATA, { constant : true }),
|
||||
separator : new type.UInt8(0x80, { constant : true })
|
||||
};
|
||||
return new type.Component(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common X224 Automata
|
||||
* @param presentation {Layer} presentation layer
|
||||
*/
|
||||
function X224(transport) {
|
||||
this.transport = transport;
|
||||
this.requestedProtocol = Protocols.PROTOCOL_SSL | Protocols.PROTOCOL_HYBRID;
|
||||
this.selectedProtocol = Protocols.PROTOCOL_SSL | Protocols.PROTOCOL_HYBRID;
|
||||
|
||||
var self = this;
|
||||
this.transport.on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('error', function (err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
//inherit from Layer
|
||||
inherits(X224, events.EventEmitter);
|
||||
|
||||
/**
|
||||
* Main data received function
|
||||
* after connection sequence
|
||||
* @param s {type.Stream} stream formated from transport layer
|
||||
*/
|
||||
X224.prototype.recvData = function(s) {
|
||||
// check header
|
||||
x224DataHeader().read(s);
|
||||
this.emit('data', s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Format message from x224 layer to transport layer
|
||||
* @param message {type}
|
||||
* @returns {type.Component} x224 formated message
|
||||
*/
|
||||
X224.prototype.send = function(message) {
|
||||
this.transport.send(new type.Component([x224DataHeader(), message]));
|
||||
};
|
||||
|
||||
/**
|
||||
* Client x224 automata
|
||||
* @param transport {events.EventEmitter} (bind data events)
|
||||
*/
|
||||
function Client(transport, config) {
|
||||
this.config = config;
|
||||
X224.call(this, transport);
|
||||
}
|
||||
|
||||
//inherit from X224 automata
|
||||
inherits(Client, X224);
|
||||
|
||||
/**
|
||||
* Client automata connect event
|
||||
*/
|
||||
Client.prototype.connect = function() {
|
||||
var message = clientConnectionRequestPDU(null, new type.BinaryString());
|
||||
message.obj.protocolNeg.obj.type.value = NegotiationType.TYPE_RDP_NEG_REQ;
|
||||
message.obj.protocolNeg.obj.result.value = this.requestedProtocol;
|
||||
this.transport.send(message);
|
||||
|
||||
// next state wait connection confirm packet
|
||||
var self = this;
|
||||
this.transport.once('data', function(s) {
|
||||
self.recvConnectionConfirm(s);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* close stack
|
||||
*/
|
||||
Client.prototype.close = function() {
|
||||
this.transport.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive connection from server
|
||||
* @param s {Stream}
|
||||
*/
|
||||
Client.prototype.recvConnectionConfirm = function(s) {
|
||||
var message = serverConnectionConfirm().read(s);
|
||||
|
||||
if (message.obj.protocolNeg.obj.type.value == NegotiationType.TYPE_RDP_NEG_FAILURE) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_X224_NEG_FAILURE',
|
||||
'Failure code:' + message.obj.protocolNeg.obj.result.value + " (see https://msdn.microsoft.com/en-us/library/cc240507.aspx)");
|
||||
}
|
||||
|
||||
if (message.obj.protocolNeg.obj.type.value == NegotiationType.TYPE_RDP_NEG_RSP) {
|
||||
this.selectedProtocol = message.obj.protocolNeg.obj.result.value;
|
||||
}
|
||||
|
||||
if ([Protocols.PROTOCOL_HYBRID_EX].indexOf(this.selectedProtocol) !== -1) {
|
||||
throw new error.ProtocolError('NODE_RDP_PROTOCOL_X224_NLA_NOT_SUPPORTED');
|
||||
}
|
||||
|
||||
if (this.selectedProtocol == Protocols.PROTOCOL_RDP) {
|
||||
log.debug("RDP standard security selected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selectedProtocol == Protocols.PROTOCOL_HYBRID) {
|
||||
log.debug("NLA security layer selected");
|
||||
var self = this;
|
||||
var transportEx = this.transport.transport;
|
||||
this.transport.transport.startTLS(function () {
|
||||
//console.log('TLS connected, start cssp_connect()');
|
||||
var NLA = require('./nla');
|
||||
self.nla = new NLA(transportEx, function () { self.nlaCompleted(); }, self.config.domain, self.config.userName, self.config.password);
|
||||
self.nla.sendNegotiateMessage();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// finish connection sequence
|
||||
var self = this;
|
||||
this.transport.on('data', function(s) {
|
||||
self.recvData(s);
|
||||
});
|
||||
|
||||
if (this.selectedProtocol == Protocols.PROTOCOL_SSL) {
|
||||
log.debug("SSL standard security selected");
|
||||
this.transport.transport.startTLS(function() {
|
||||
self.emit('connect', self.selectedProtocol);
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when NLA is completed
|
||||
*/
|
||||
Client.prototype.nlaCompleted = function () {
|
||||
const self = this;
|
||||
delete self.nla;
|
||||
this.transport.on('data', function (s) { self.recvData(s); });
|
||||
this.emit('connect', this.selectedProtocol);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Server x224 automata
|
||||
*/
|
||||
function Server(transport, keyFilePath, crtFilePath) {
|
||||
X224.call(this, transport);
|
||||
this.keyFilePath = keyFilePath;
|
||||
this.crtFilePath = crtFilePath;
|
||||
var self = this;
|
||||
this.transport.once('data', function (s) {
|
||||
self.recvConnectionRequest(s);
|
||||
});
|
||||
}
|
||||
|
||||
//inherit from X224 automata
|
||||
inherits(Server, X224);
|
||||
|
||||
/**
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240470.aspx
|
||||
* @param s {type.Stream}
|
||||
*/
|
||||
Server.prototype.recvConnectionRequest = function (s) {
|
||||
var request = clientConnectionRequestPDU().read(s);
|
||||
if (!request.obj.protocolNeg.isReaded) {
|
||||
throw new Error('NODE_RDP_PROTOCOL_X224_NO_BASIC_SECURITY_LAYER');
|
||||
}
|
||||
|
||||
this.requestedProtocol = request.obj.protocolNeg.obj.result.value;
|
||||
this.selectedProtocol = this.requestedProtocol & Protocols.PROTOCOL_SSL;
|
||||
|
||||
if (!(this.selectedProtocol & Protocols.PROTOCOL_SSL)) {
|
||||
var confirm = serverConnectionConfirm();
|
||||
confirm.obj.protocolNeg.obj.type.value = NegociationType.TYPE_RDP_NEG_FAILURE;
|
||||
confirm.obj.protocolNeg.obj.result.value = NegotiationFailureCode.SSL_REQUIRED_BY_SERVER;
|
||||
this.transport.send(confirm);
|
||||
this.close();
|
||||
}
|
||||
else {
|
||||
this.sendConnectionConfirm();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Start SSL connection if needed
|
||||
* @see http://msdn.microsoft.com/en-us/library/cc240501.aspx
|
||||
*/
|
||||
Server.prototype.sendConnectionConfirm = function () {
|
||||
var confirm = serverConnectionConfirm();
|
||||
confirm.obj.protocolNeg.obj.type.value = NegotiationType.TYPE_RDP_NEG_RSP;
|
||||
confirm.obj.protocolNeg.obj.result.value = this.selectedProtocol;
|
||||
this.transport.send(confirm);
|
||||
|
||||
// finish connection sequence
|
||||
var self = this;
|
||||
this.transport.on('data', function(s) {
|
||||
self.recvData(s);
|
||||
});
|
||||
|
||||
this.transport.transport.listenTLS(this.keyFilePath, this.crtFilePath, function() {
|
||||
log.debug('start SSL connection');
|
||||
self.emit('connect', self.requestedProtocol);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Module exports
|
||||
*/
|
||||
module.exports = {
|
||||
Client : Client,
|
||||
Server : Server
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue