1
0
Fork 0
mirror of https://github.com/Ylianst/MeshCentral.git synced 2025-03-09 15:40:18 +00:00

Added Web based RDP support with NLA, #3867 and #3914

This commit is contained in:
Ylian Saint-Hilaire 2022-04-29 11:13:58 -07:00
parent f88d3063fe
commit db06ec1975
37 changed files with 28174 additions and 44 deletions

540
rdp/protocol/t125/gcc.js Normal file
View 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
};

View 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
View 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
View 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
};