ssh2
Advanced tools
Comparing version 0.2.18 to 0.2.19
@@ -505,3 +505,6 @@ var inherits = require('util').inherits, | ||
function ChannelStream(channel) { | ||
var self = this; | ||
var self = this, | ||
emittedFinish = false, | ||
emittedClose = false, | ||
emittedEnd = false; | ||
this.readable = true; | ||
@@ -517,2 +520,5 @@ this.writable = true; | ||
this._sockOnEnd = function() { | ||
!emittedFinish && self.emit('finish'); | ||
!emittedEnd && self.emit('end'); | ||
!emittedClose && self.emit('close'); | ||
self.writable = false; | ||
@@ -523,2 +529,8 @@ self.readable = false; | ||
}; | ||
function onFinish() { emittedFinish = true; } | ||
this.once('finish', onFinish); | ||
function onEnd() { emittedEnd = true; } | ||
this.once('end', onEnd); | ||
function onClose() { emittedClose = true; } | ||
this.once('close', onClose); | ||
channel._conn._sock.once('end', this._sockOnEnd); | ||
@@ -646,8 +658,6 @@ channel._conn._sock.once('close', this._sockOnEnd); | ||
return false; | ||
} else { | ||
if (extended) | ||
return this._channel._sendData(data); | ||
else | ||
return this._channel._sendData(data, extended); | ||
} | ||
} else if (extended) | ||
return this._channel._sendData(data, extended); | ||
else | ||
return this._channel._sendData(data); | ||
} else | ||
@@ -654,0 +664,0 @@ throw new Error('Unexpected data type: ' + typeof data); |
@@ -209,2 +209,13 @@ var i = 0, keys, len; | ||
if (process.versions.openssl >= '1.0.1') { | ||
if (process.version >= 'v0.11.12') { | ||
// node v0.11.12 introduced support for setting AAD, which is needed for | ||
// AES-GCM in SSH2 | ||
CIPHER = [ | ||
// http://tools.ietf.org/html/rfc5647 | ||
'aes128-gcm', | ||
'aes128-gcm@openssh.com', | ||
'aes256-gcm', | ||
'aes256-gcm@openssh.com' | ||
].concat(CIPHER); | ||
} | ||
CIPHER = [ | ||
@@ -217,2 +228,17 @@ // http://tools.ietf.org/html/rfc4344#section-4 | ||
CIPHER_LIST = new Buffer(CIPHER.join(',')); | ||
// http://tools.ietf.org/html/rfc5647#section-5.1: | ||
// If AES-GCM is selected as the encryption algorithm for a given | ||
// tunnel, AES-GCM MUST also be selected as the Message Authentication | ||
// Code (MAC) algorithm. ***Conversely, if AES-GCM is selected as the MAC | ||
// algorithm, it MUST also be selected as the encryption algorithm.*** | ||
// Note: @openssh.com versions deviate from the above rule | ||
/*HMAC = [ | ||
// http://tools.ietf.org/html/rfc5647 | ||
'aes128-gcm', | ||
'aes128-gcm@openssh.com', | ||
'aes256-gcm', | ||
'aes256-gcm@openssh.com' | ||
].concat(HMAC); | ||
HMAC_LIST = new Buffer(HMAC.join(','));*/ | ||
} | ||
@@ -239,2 +265,6 @@ | ||
// ciphers | ||
'aes128-gcm': 'aes-128-gcm', | ||
'aes256-gcm': 'aes-256-gcm', | ||
'aes128-gcm@openssh.com': 'aes-128-gcm', | ||
'aes256-gcm@openssh.com': 'aes-256-gcm', | ||
'3des-cbc': 'des-ede3-cbc', | ||
@@ -255,5 +285,5 @@ 'blowfish-cbc': 'bf-cbc', | ||
'camellia256-cbc': 'camellia-256-cbc', | ||
'camellia128-cbc@openssh.org': 'camellia-128-cbc', | ||
'camellia192-cbc@openssh.org': 'camellia-192-cbc', | ||
'camellia256-cbc@openssh.org': 'camellia-256-cbc', | ||
'camellia128-cbc@openssh.com': 'camellia-128-cbc', | ||
'camellia192-cbc@openssh.com': 'camellia-192-cbc', | ||
'camellia256-cbc@openssh.com': 'camellia-256-cbc', | ||
'3des-ctr': 'des-ede3', | ||
@@ -268,5 +298,5 @@ 'blowfish-ctr': 'bf-ecb', | ||
'camellia256-ctr': 'camellia-256-ecb', | ||
'camellia128-ctr@openssh.org': 'camellia-128-ecb', | ||
'camellia192-ctr@openssh.org': 'camellia-192-ecb', | ||
'camellia256-ctr@openssh.org': 'camellia-256-ecb', | ||
'camellia128-ctr@openssh.com': 'camellia-128-ecb', | ||
'camellia192-ctr@openssh.com': 'camellia-192-ecb', | ||
'camellia256-ctr@openssh.com': 'camellia-256-ecb', | ||
// hmac | ||
@@ -273,0 +303,0 @@ 'hmac-sha1-96': 'sha1', |
// TODO: * Filter control codes from strings | ||
// (as per http://tools.ietf.org/html/rfc4251#section-9.2) | ||
var crypto = require('crypto'); | ||
var crypto = require('crypto'), | ||
inherits = require('util').inherits, | ||
EventEmitter = require('events').EventEmitter; | ||
var StreamSearch = require('streamsearch'); | ||
var consts = require('./Parser.constants'); | ||
var inherits = require('util').inherits, | ||
EventEmitter = require('events').EventEmitter; | ||
var consts = require('./Parser.constants'), | ||
isGCM = require('./utils').isGCM, | ||
iv_inc = require('./utils').iv_inc; | ||
@@ -13,7 +15,5 @@ var MESSAGE = consts.MESSAGE, | ||
CHANNEL_OPEN_FAILURE = consts.CHANNEL_OPEN_FAILURE, | ||
SSH_TO_OPENSSL = consts.SSH_TO_OPENSSL; | ||
// parser states | ||
var I = 0; | ||
var STATE_INIT = I++, | ||
SSH_TO_OPENSSL = consts.SSH_TO_OPENSSL, | ||
I = 0, | ||
STATE_INIT = I++, | ||
STATE_GREETING = I++, | ||
@@ -25,7 +25,5 @@ STATE_HEADER = I++, | ||
STATE_PACKETDATAVERIFY = I++, | ||
STATE_PACKETDATAAFTER = I++; | ||
var MAX_SEQNO = 4294967295; | ||
var EXP_TYPE_HEADER = 0, | ||
STATE_PACKETDATAAFTER = I++, | ||
MAX_SEQNO = 4294967295, | ||
EXP_TYPE_HEADER = 0, | ||
EXP_TYPE_LF = 1, | ||
@@ -45,3 +43,3 @@ EXP_TYPE_BYTES = 2; // waits until n bytes have been seen | ||
var i = start, buffer, skipDecrypt = false, buf, self = this, p = i; | ||
var i = start, buffer, skipDecrypt = false, buf, self = this, p = i, r; | ||
@@ -65,5 +63,7 @@ while (true) { | ||
} else if (this._expectType === EXP_TYPE_HEADER) { | ||
this._ss.push(b); | ||
if (this._expectType !== undefined) | ||
r = this._ss.push(b); | ||
if (this._expectType !== undefined) { | ||
i += r; | ||
continue; | ||
} | ||
} else if (this._expectType === EXP_TYPE_LF) { | ||
@@ -156,12 +156,22 @@ if (b[i] === 0x0A) { | ||
this.debug&&this.debug('DEBUG: Parser: STATE_PACKET'); | ||
if (this._decrypt) | ||
var doDecryptGCM = (this._decrypt && isGCM(this._decryptType)); | ||
if (this._decrypt && !isGCM(this._decryptType)) | ||
buffer = this.decrypt(buffer); | ||
this._pktLen = buffer.readUInt32BE(0, true); | ||
this._padLen = buffer[4]; | ||
var remainLen = this._pktLen + 4 - this._decryptSize; | ||
this.debug&&this.debug('DEBUG: Parser: pktLen:' + this._pktLen | ||
+ ',padLen:' + this._padLen | ||
+ ',remainLen:' + remainLen); | ||
if (doDecryptGCM) { | ||
this._decrypt.setAAD(buffer.slice(0, 4)); | ||
this.debug&&this.debug('DEBUG: Parser: pktLen:' + this._pktLen | ||
+ ',remainLen:' + remainLen); | ||
} else { | ||
this._padLen = buffer[4]; | ||
this.debug&&this.debug('DEBUG: Parser: pktLen:' + this._pktLen | ||
+ ',padLen:' + this._padLen | ||
+ ',remainLen:' + remainLen); | ||
} | ||
if (remainLen > 0) { | ||
this._pktExtra = buffer.slice(5); | ||
if (doDecryptGCM) | ||
this._pktExtra = buffer.slice(4); | ||
else | ||
this._pktExtra = buffer.slice(5); | ||
// grab the rest of the packet | ||
@@ -180,3 +190,4 @@ this.expect(EXP_TYPE_BYTES, remainLen); | ||
this.debug&&this.debug('DEBUG: Parser: STATE_PACKETDATA'); | ||
if (this._decrypt && !skipDecrypt) | ||
var doDecryptGCM = (this._decrypt && isGCM(this._decryptType)); | ||
if (this._decrypt && !skipDecrypt && !doDecryptGCM) | ||
buffer = this.decrypt(buffer); | ||
@@ -193,3 +204,6 @@ else if (skipDecrypt) | ||
// entire message fit into one block | ||
buf = buffer.slice(5); | ||
if (doDecryptGCM) | ||
buf = buffer.slice(4); | ||
else | ||
buf = buffer.slice(5); | ||
this._payload = buffer.slice(5, 5 + padStart); | ||
@@ -633,12 +647,27 @@ } | ||
this.debug&&this.debug('DEBUG: Parser: Verifying MAC'); | ||
var calcHmac = crypto.createHmac(SSH_TO_OPENSSL[this._hmac], this._hmacKey); | ||
if (isGCM(this._decryptType)) { | ||
this._decrypt.setAuthTag(hmac); | ||
var payload = new Buffer(this._decrypt.update(this._packet, 'binary', 'binary'), 'binary'); | ||
this._payload = payload.slice(1, this._packet.length + 4 - payload[0]); | ||
this._decrypt.final('binary'); | ||
iv_inc(this._decryptIV); | ||
this._decrypt = crypto.createDecipheriv( | ||
SSH_TO_OPENSSL[this._decryptType], | ||
this._decryptKey, | ||
this._decryptIV | ||
); | ||
this._decrypt.setAutoPadding(false); | ||
return true; | ||
} else { | ||
var calcHmac = crypto.createHmac(SSH_TO_OPENSSL[this._hmac], this._hmacKey); | ||
this._hmacBufCompute.writeUInt32BE(this._seqno, 0, true); | ||
this._hmacBufCompute.writeUInt32BE(this._pktLen, 4, true); | ||
this._hmacBufCompute[8] = this._padLen; | ||
this._hmacBufCompute.writeUInt32BE(this._seqno, 0, true); | ||
this._hmacBufCompute.writeUInt32BE(this._pktLen, 4, true); | ||
this._hmacBufCompute[8] = this._padLen; | ||
calcHmac.update(this._hmacBufCompute); | ||
calcHmac.update(this._packet); | ||
calcHmac.update(this._hmacBufCompute); | ||
calcHmac.update(this._packet); | ||
return (calcHmac.digest('binary') === hmac.toString('binary')); | ||
return (calcHmac.digest('binary') === hmac.toString('binary')); | ||
} | ||
}; | ||
@@ -672,3 +701,6 @@ | ||
this._decrypt = false; | ||
this._decryptIV = undefined; | ||
this._decryptKey = undefined; | ||
this._decryptBuf = undefined; | ||
this._decryptType = undefined; | ||
this._authMethod = undefined; | ||
@@ -675,0 +707,0 @@ |
{ "name": "ssh2", | ||
"version": "0.2.18", | ||
"version": "0.2.19", | ||
"author": "Brian White <mscdex@mscdex.net>", | ||
@@ -4,0 +4,0 @@ "description": "An SSH2 client module written in pure JavaScript for node.js", |
@@ -312,2 +312,41 @@ Description | ||
* Connection hopping: | ||
```javascript | ||
var Connection = require('ssh2'); | ||
var conn1 = new Connection(), | ||
conn2 = new Connection(); | ||
conn1.on('ready', function() { | ||
console.log('FIRST :: connection ready'); | ||
conn1.exec('nc 192.168.1.2 22', function(err, stream) { | ||
if (err) return console.log('FIRST :: exec error: ' + err); | ||
conn2.connect({ | ||
sock: stream, | ||
username: 'user2', | ||
password: 'password2', | ||
}); | ||
}); | ||
}); | ||
conn1.connect({ | ||
host: '192.168.1.1', | ||
username: 'user1', | ||
password: 'password1', | ||
}); | ||
conn2.on('ready', function() { | ||
console.log('SECOND :: connection ready'); | ||
conn2.exec('uptime', function(err, stream) { | ||
if (err) return console.log('SECOND :: exec error: ' + err); | ||
stream.on('data', function(data) { | ||
console.log(data.toString()); | ||
}); | ||
stream.on('end', function() { | ||
conn1.end(); // close parent (and this) connection | ||
}); | ||
}); | ||
}); | ||
``` | ||
* Invoke an arbitrary subsystem (netconf in this example): | ||
@@ -355,2 +394,3 @@ | ||
API | ||
@@ -422,2 +462,4 @@ === | ||
* **readyTimeout** - < _integer_ > - How often (in milliseconds) to wait for the SSH handshake to complete. **Default:** (10000) | ||
* **sock** - < _ReadableStream_ > - A _ReadableStream_ to use for communicating with the server instead of creating and using a new TCP connection (useful for connection hopping). | ||
@@ -424,0 +466,0 @@ |
Sorry, the diff of this file is too big to display
283320
16
5830
695