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

704
rdp/protocol/pdu/caps.js Normal file
View 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

File diff suppressed because it is too large Load diff

402
rdp/protocol/pdu/global.js Normal file
View 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
View 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
View 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
View 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
};