sec.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. /*
  2. * Copyright (c) 2014-2015 Sylvain Peyrefitte
  3. *
  4. * This file is part of node-rdpjs.
  5. *
  6. * node-rdpjs is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. var inherits = require('util').inherits;
  20. var crypto = require('crypto');
  21. var events = require('events');
  22. var type = require('../../core').type;
  23. var error = require('../../core').error;
  24. var log = require('../../core').log;
  25. var gcc = require('../t125/gcc');
  26. var lic = require('./lic');
  27. var cert = require('../cert');
  28. var rsa = require('../../security').rsa;
  29. /**
  30. * @see http://msdn.microsoft.com/en-us/library/cc240579.aspx
  31. */
  32. var SecurityFlag = {
  33. SEC_EXCHANGE_PKT : 0x0001,
  34. SEC_TRANSPORT_REQ : 0x0002,
  35. RDP_SEC_TRANSPORT_RSP : 0x0004,
  36. SEC_ENCRYPT : 0x0008,
  37. SEC_RESET_SEQNO : 0x0010,
  38. SEC_IGNORE_SEQNO : 0x0020,
  39. SEC_INFO_PKT : 0x0040,
  40. SEC_LICENSE_PKT : 0x0080,
  41. SEC_LICENSE_ENCRYPT_CS : 0x0200,
  42. SEC_LICENSE_ENCRYPT_SC : 0x0200,
  43. SEC_REDIRECTION_PKT : 0x0400,
  44. SEC_SECURE_CHECKSUM : 0x0800,
  45. SEC_AUTODETECT_REQ : 0x1000,
  46. SEC_AUTODETECT_RSP : 0x2000,
  47. SEC_HEARTBEAT : 0x4000,
  48. SEC_FLAGSHI_VALID : 0x8000
  49. };
  50. /**
  51. * @see https://msdn.microsoft.com/en-us/library/cc240475.aspx
  52. */
  53. var InfoFlag = {
  54. INFO_MOUSE : 0x00000001,
  55. INFO_DISABLECTRLALTDEL : 0x00000002,
  56. INFO_AUTOLOGON : 0x00000008,
  57. INFO_UNICODE : 0x00000010,
  58. INFO_MAXIMIZESHELL : 0x00000020,
  59. INFO_LOGONNOTIFY : 0x00000040,
  60. INFO_COMPRESSION : 0x00000080,
  61. INFO_ENABLEWINDOWSKEY : 0x00000100,
  62. INFO_REMOTECONSOLEAUDIO : 0x00002000,
  63. INFO_FORCE_ENCRYPTED_CS_PDU : 0x00004000,
  64. INFO_RAIL : 0x00008000,
  65. INFO_LOGONERRORS : 0x00010000,
  66. INFO_MOUSE_HAS_WHEEL : 0x00020000,
  67. INFO_PASSWORD_IS_SC_PIN : 0x00040000,
  68. INFO_NOAUDIOPLAYBACK : 0x00080000,
  69. INFO_USING_SAVED_CREDS : 0x00100000,
  70. INFO_AUDIOCAPTURE : 0x00200000,
  71. INFO_VIDEO_DISABLE : 0x00400000,
  72. INFO_CompressionTypeMask : 0x00001E00
  73. };
  74. /**
  75. * @see https://msdn.microsoft.com/en-us/library/cc240476.aspx
  76. */
  77. var AfInet = {
  78. AfInet : 0x00002,
  79. AF_INET6 : 0x0017
  80. };
  81. /**
  82. * @see https://msdn.microsoft.com/en-us/library/cc240476.aspx
  83. */
  84. var PerfFlag = {
  85. PERF_DISABLE_WALLPAPER : 0x00000001,
  86. PERF_DISABLE_FULLWINDOWDRAG : 0x00000002,
  87. PERF_DISABLE_MENUANIMATIONS : 0x00000004,
  88. PERF_DISABLE_THEMING : 0x00000008,
  89. PERF_DISABLE_CURSOR_SHADOW : 0x00000020,
  90. PERF_DISABLE_CURSORSETTINGS : 0x00000040,
  91. PERF_ENABLE_FONT_SMOOTHING : 0x00000080,
  92. PERF_ENABLE_DESKTOP_COMPOSITION : 0x00000100
  93. };
  94. /**
  95. * @see http://msdn.microsoft.com/en-us/library/cc241992.aspx
  96. * @param input {Buffer} Binary data
  97. * @param salt {Buffer} salt for context call
  98. * @param salt1 {Buffer} another salt (ex : client random)
  99. * @param salt2 {Buffer} another salt (ex : server random)
  100. * @return {Buffer}
  101. */
  102. function saltedHash(input, salt, salt1, salt2) {
  103. var sha1Digest = crypto.createHash('sha1');
  104. sha1Digest.update(input);
  105. sha1Digest.update(salt.slice(0, 48));
  106. sha1Digest.update(salt1);
  107. sha1Digest.update(salt2);
  108. var sha1Sig = sha1Digest.digest();
  109. var md5Digest = crypto.createHash('md5');
  110. md5Digest.update(salt.slice(0, 48));
  111. md5Digest.update(sha1Sig);
  112. return md5Digest.digest();
  113. }
  114. /**
  115. * @param key {Buffer} secret
  116. * @param random1 {Buffer} client random
  117. * @param random2 {Buffer} server random
  118. * @returns {Buffer}
  119. */
  120. function finalHash (key, random1, random2) {
  121. var md5Digest = crypto.createHash('md5');
  122. md5Digest.update(key);
  123. md5Digest.update(random1);
  124. md5Digest.update(random2);
  125. return md5Digest.digest();
  126. }
  127. /**
  128. * @see http://msdn.microsoft.com/en-us/library/cc241992.aspx
  129. * @param secret {Buffer} secret
  130. * @param random1 {Buffer} client random
  131. * @param random2 {Buffer} server random
  132. * @returns {Buffer}
  133. */
  134. function masterSecret (secret, random1, random2) {
  135. var sh1 = saltedHash(Buffer.from('A'), secret, random1, random2);
  136. var sh2 = saltedHash(Buffer.from('BB'), secret, random1, random2);
  137. var sh3 = saltedHash(Buffer.from('CCC'), secret, random1, random2);
  138. var ms = Buffer.alloc(sh1.length + sh2.length + sh3.length);
  139. sh1.copy(ms);
  140. sh2.copy(ms, sh1.length);
  141. sh3.copy(ms, sh1.length + sh2.length);
  142. return ms;
  143. }
  144. /**
  145. * @see http://msdn.microsoft.com/en-us/library/cc241995.aspx
  146. * @param macSaltKey {Buffer} key
  147. * @param data {Buffer} data
  148. * @returns {Buffer}
  149. */
  150. function macData(macSaltKey, data) {
  151. var salt1 = Buffer.alloc(40);
  152. salt1.fill(0x36);
  153. var salt2 = Buffer.alloc(48);
  154. salt2.fill(0x5c);
  155. var dataLength = new type.UInt32Le(data.length).toStream().buffer;
  156. var sha1 = crypto.createHash('sha1');
  157. sha1.update(macSaltKey);
  158. sha1.update(salt1);
  159. sha1.update(dataLength);
  160. sha1.update(data);
  161. var sha1Digest = sha1.digest();
  162. var md5 = crypto.createHash('md5');
  163. md5.update(macSaltKey);
  164. md5.update(salt2);
  165. md5.update(sha1Digest);
  166. return md5.digest();
  167. }
  168. /**
  169. * RDP client informations
  170. * @param extendedInfoConditional {boolean} true if RDP5+
  171. * @returns {type.Component}
  172. */
  173. function rdpInfos(extendedInfoConditional) {
  174. var self = {
  175. codePage : new type.UInt32Le(),
  176. flag : new type.UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_DISABLECTRLALTDEL | InfoFlag.INFO_ENABLEWINDOWSKEY),
  177. cbDomain : new type.UInt16Le(function() {
  178. return self.domain.size() - 2;
  179. }),
  180. cbUserName : new type.UInt16Le(function() {
  181. return self.userName.size() - 2;
  182. }),
  183. cbPassword : new type.UInt16Le(function() {
  184. return self.password.size() - 2;
  185. }),
  186. cbAlternateShell : new type.UInt16Le(function() {
  187. return self.alternateShell.size() - 2;
  188. }),
  189. cbWorkingDir : new type.UInt16Le(function() {
  190. return self.workingDir.size() - 2;
  191. }),
  192. domain : new type.BinaryString(Buffer.from('\x00', 'ucs2'),{ readLength : new type.CallableValue(function() {
  193. return self.cbDomain.value + 2;
  194. })}),
  195. userName : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
  196. return self.cbUserName.value + 2;
  197. })}),
  198. password : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function () {
  199. return self.cbPassword.value + 2;
  200. })}),
  201. alternateShell : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
  202. return self.cbAlternateShell.value + 2;
  203. })}),
  204. workingDir : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
  205. return self.cbWorkingDir.value + 2;
  206. })}),
  207. extendedInfo : rdpExtendedInfos({ conditional : extendedInfoConditional })
  208. };
  209. return new type.Component(self);
  210. }
  211. /**
  212. * RDP client extended informations present in RDP5+
  213. * @param opt
  214. * @returns {type.Component}
  215. */
  216. function rdpExtendedInfos(opt) {
  217. var self = {
  218. clientAddressFamily : new type.UInt16Le(AfInet.AfInet),
  219. cbClientAddress : new type.UInt16Le(function() {
  220. return self.clientAddress.size();
  221. }),
  222. clientAddress : new type.BinaryString(Buffer.from('\x00', 'ucs2'),{ readLength : new type.CallableValue(function() {
  223. return self.cbClientAddress;
  224. }) }),
  225. cbClientDir : new type.UInt16Le(function() {
  226. return self.clientDir.size();
  227. }),
  228. clientDir : new type.BinaryString(Buffer.from('\x00', 'ucs2'), { readLength : new type.CallableValue(function() {
  229. return self.cbClientDir;
  230. }) }),
  231. clientTimeZone : new type.BinaryString(Buffer.from(Array(172 + 1).join("\x00"))),
  232. clientSessionId : new type.UInt32Le(),
  233. performanceFlags : new type.UInt32Le()
  234. };
  235. return new type.Component(self, opt);
  236. }
  237. /**
  238. * Header of security header
  239. * @returns {type.Component}
  240. */
  241. function securityHeader() {
  242. var self = {
  243. securityFlag : new type.UInt16Le(),
  244. securityFlagHi : new type.UInt16Le()
  245. };
  246. return new type.Component(self);
  247. }
  248. /**
  249. * Security layer
  250. * @param transport {events.EventEmitter}
  251. */
  252. function Sec(transport, fastPathTransport) {
  253. this.transport = transport;
  254. this.fastPathTransport = fastPathTransport;
  255. // init at connect event from transport layer
  256. this.gccClient = null;
  257. this.gccServer = null;
  258. var self = this;
  259. this.infos = rdpInfos(function() {
  260. return self.gccClient.core.rdpVersion.value === gcc.VERSION.RDP_VERSION_5_PLUS;
  261. });
  262. this.machineName = '';
  263. // basic encryption
  264. this.enableEncryption = false;
  265. if (this.fastPathTransport) {
  266. this.fastPathTransport.on('fastPathData', function (secFlag, s) {
  267. self.recvFastPath(secFlag, s);
  268. });
  269. }
  270. };
  271. //inherit from Layer
  272. inherits(Sec, events.EventEmitter);
  273. /**
  274. * Send message with security header
  275. * @param flag {integer} security flag
  276. * @param data {type.*} message
  277. */
  278. Sec.prototype.sendFlagged = function(flag, data) {
  279. this.transport.send('global', new type.Component([
  280. new type.UInt16Le(flag),
  281. new type.UInt16Le(),
  282. data
  283. ]));
  284. };
  285. /**
  286. * Main send function
  287. * @param message {type.*} message to send
  288. */
  289. Sec.prototype.send = function(message) {
  290. if (this.enableEncryption) {
  291. throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_SEC_ENCRYPT_NOT_IMPLEMENTED');
  292. }
  293. this.transport.send('global', message);
  294. };
  295. /**
  296. * Main receive function
  297. * @param s {type.Stream}
  298. */
  299. Sec.prototype.recv = function(s) {
  300. if (this.enableEncryption) {
  301. throw new error.FatalError('NODE_RDP_PROTOCOL_PDU_SEC_ENCRYPT_NOT_IMPLEMENTED');
  302. }
  303. // not support yet basic RDP security layer
  304. this.emit('data', s);
  305. };
  306. /**
  307. * Receive fast path data
  308. * @param secFlag {integer} security flag
  309. * @param s {type.Stream}
  310. */
  311. Sec.prototype.recvFastPath = function (secFlag, s) {
  312. // transparent because basic RDP security layer not implemented
  313. this.emit('fastPathData', secFlag, s);
  314. };
  315. /**
  316. * Client security layer
  317. * @param transport {events.EventEmitter}
  318. */
  319. function Client(transport, fastPathTransport) {
  320. Sec.call(this, transport, fastPathTransport);
  321. // for basic RDP layer (in futur)
  322. this.enableSecureCheckSum = false;
  323. var self = this;
  324. this.transport.on('connect', function(gccClient, gccServer, userId, channels) {
  325. self.connect(gccClient, gccServer, userId, channels);
  326. }).on('close', function() {
  327. self.emit('close');
  328. }).on('error', function (err) {
  329. self.emit('error', err);
  330. });
  331. };
  332. //inherit from Layer
  333. inherits(Client, Sec);
  334. /**
  335. * Connect event
  336. */
  337. Client.prototype.connect = function(gccClient, gccServer, userId, channels) {
  338. //init gcc information
  339. this.gccClient = gccClient;
  340. this.gccServer = gccServer;
  341. this.userId = userId;
  342. this.channelId = channels.find(function(e) {
  343. if(e.name === 'global') return true;
  344. }).id;
  345. this.sendInfoPkt();
  346. };
  347. /**
  348. * close stack
  349. */
  350. Client.prototype.close = function() {
  351. this.transport.close();
  352. };
  353. /**
  354. * Send main information packet
  355. * VIP (very important packet) because contain credentials
  356. */
  357. Client.prototype.sendInfoPkt = function() {
  358. this.sendFlagged(SecurityFlag.SEC_INFO_PKT, this.infos);
  359. var self = this;
  360. this.transport.once('global', function(s) {
  361. self.recvLicense(s);
  362. });
  363. };
  364. function reverse(buffer) {
  365. var result = Buffer.alloc(buffer.length);
  366. for(var i = 0; i < buffer.length; i++) {
  367. result.writeUInt8(buffer.readUInt8(buffer.length - 1 - i), i);
  368. }
  369. return result;
  370. }
  371. /**
  372. * Send a valid license request
  373. * @param licenseRequest {object(lic.serverLicenseRequest)} license requets infos
  374. */
  375. Client.prototype.sendClientNewLicenseRequest = function(licenseRequest) {
  376. log.debug('new license request');
  377. var serverRandom = licenseRequest.serverRandom.value;
  378. // read server certificate
  379. var s = new type.Stream(licenseRequest.serverCertificate.obj.blobData.value);
  380. var certificate = cert.certificate().read(s).obj;
  381. var publicKey = certificate.certData.obj.getPublicKey();
  382. var clientRandom = crypto.randomBytes(32);
  383. var preMasterSecret = crypto.randomBytes(48);
  384. var mSecret = masterSecret(preMasterSecret, clientRandom, serverRandom);
  385. var sessionKeyBlob = masterSecret(mSecret, serverRandom, clientRandom);
  386. this.licenseMacSalt = sessionKeyBlob.slice(0, 16)
  387. this.licenseKey = finalHash(sessionKeyBlob.slice(16, 32), clientRandom, serverRandom);
  388. var request = lic.clientNewLicenseRequest();
  389. request.obj.clientRandom.value = clientRandom;
  390. var preMasterSecretEncrypted = reverse(rsa.encrypt(reverse(preMasterSecret), publicKey));
  391. var preMasterSecretEncryptedPadded = Buffer.alloc(preMasterSecretEncrypted.length + 8);
  392. preMasterSecretEncryptedPadded.fill(0);
  393. preMasterSecretEncrypted.copy(preMasterSecretEncryptedPadded);
  394. request.obj.encryptedPreMasterSecret.obj.blobData.value = preMasterSecretEncryptedPadded;
  395. request.obj.ClientMachineName.obj.blobData.value = this.infos.obj.userName.value;
  396. request.obj.ClientUserName.obj.blobData.value = Buffer.from(this.machineName + '\x00');
  397. this.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.licensePacket(request));
  398. };
  399. /**
  400. * Send a valid license request
  401. * @param platformChallenge {object(lic.serverPlatformChallenge)} platform challenge
  402. */
  403. Client.prototype.sendClientChallengeResponse = function(platformChallenge) {
  404. log.debug('challenge license');
  405. var serverEncryptedChallenge = platformChallenge.encryptedPlatformChallenge.obj.blobData.value;
  406. var serverChallenge = crypto.createDecipheriv('rc4', this.licenseKey, '').update(serverEncryptedChallenge);
  407. if (serverChallenge.toString('ucs2') !== 'TEST\x00') {
  408. throw new error.ProtocolError('NODE_RDP_PROTOCOL_PDU_SEC_INVALID_LICENSE_CHALLENGE');
  409. }
  410. var hwid = new type.Component([new type.UInt32Le(2), new type.BinaryString(crypto.randomBytes(16))]).toStream().buffer;
  411. var response = lic.clientPLatformChallengeResponse();
  412. response.obj.encryptedPlatformChallengeResponse.obj.blobData.value = serverEncryptedChallenge;
  413. response.obj.encryptedHWID.obj.blobData.value = crypto.createCipheriv('rc4', this.licenseKey, '').update(hwid);
  414. var sig = Buffer.alloc(serverChallenge.length + hwid.length);
  415. serverChallenge.copy(sig);
  416. hwid.copy(sig, serverChallenge.length);
  417. response.obj.MACData.value = macData(this.licenseMacSalt, sig);
  418. this.sendFlagged(SecurityFlag.SEC_LICENSE_PKT, lic.licensePacket(response));
  419. };
  420. /**
  421. * Receive license informations
  422. * @param s {type.Stream}
  423. */
  424. Sec.prototype.recvLicense = function(s) {
  425. var header = securityHeader().read(s).obj;
  426. if (!(header.securityFlag.value & SecurityFlag.SEC_LICENSE_PKT)) {
  427. throw new error.ProtocolError('NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER');
  428. }
  429. var message = lic.licensePacket().read(s).obj;
  430. // i'm accepted
  431. if (message.bMsgtype.value === lic.MessageType.NEW_LICENSE ||
  432. (message.bMsgtype.value === lic.MessageType.ERROR_ALERT
  433. && message.licensingMessage.obj.dwErrorCode.value === lic.ErrorCode.STATUS_VALID_CLIENT
  434. && message.licensingMessage.obj.dwStateTransition.value === lic.StateTransition.ST_NO_TRANSITION)) {
  435. this.emit('connect', this.gccClient.core, this.userId, this.channelId);
  436. var self = this;
  437. this.transport.on('global', function(s) {
  438. self.recv(s);
  439. });
  440. return;
  441. }
  442. // server ask license request
  443. if (message.bMsgtype.value === lic.MessageType.LICENSE_REQUEST) {
  444. this.sendClientNewLicenseRequest(message.licensingMessage.obj);
  445. }
  446. // server send challenge
  447. if (message.bMsgtype.value === lic.MessageType.PLATFORM_CHALLENGE) {
  448. this.sendClientChallengeResponse(message.licensingMessage.obj);
  449. }
  450. var self = this;
  451. this.emit('connect', this.gccClient.core);
  452. this.transport.once('global', function (s) {
  453. self.recvLicense(s);
  454. });
  455. };
  456. /**
  457. * Module exports
  458. */
  459. module.exports = {
  460. PerfFlag : PerfFlag,
  461. InfoFlag : InfoFlag,
  462. Client : Client
  463. };