first commit
This commit is contained in:
459
build/node_modules/ssh2/test/test-openssh.js
generated
vendored
Normal file
459
build/node_modules/ssh2/test/test-openssh.js
generated
vendored
Normal file
@@ -0,0 +1,459 @@
|
||||
var Server = require('../lib/server');
|
||||
var utils = require('ssh2-streams').utils;
|
||||
|
||||
var semver = require('semver');
|
||||
|
||||
var fs = require('fs');
|
||||
var crypto = require('crypto');
|
||||
var path = require('path');
|
||||
var join = path.join;
|
||||
var assert = require('assert');
|
||||
var spawn = require('child_process').spawn;
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
var t = -1;
|
||||
var group = path.basename(__filename, '.js') + '/';
|
||||
var fixturesdir = join(__dirname, 'fixtures');
|
||||
|
||||
var CLIENT_TIMEOUT = 5000;
|
||||
var USER = 'nodejs';
|
||||
var HOST_KEY_RSA = fs.readFileSync(join(fixturesdir, 'ssh_host_rsa_key'));
|
||||
var HOST_KEY_DSA = fs.readFileSync(join(fixturesdir, 'ssh_host_dsa_key'));
|
||||
var HOST_KEY_ECDSA = fs.readFileSync(join(fixturesdir, 'ssh_host_ecdsa_key'));
|
||||
var CLIENT_KEY_RSA_PATH = join(fixturesdir, 'id_rsa');
|
||||
var CLIENT_KEY_RSA = fs.readFileSync(CLIENT_KEY_RSA_PATH);
|
||||
var CLIENT_KEY_RSA_PUB = utils.genPublicKey(utils.parseKey(CLIENT_KEY_RSA));
|
||||
var CLIENT_KEY_DSA_PATH = join(fixturesdir, 'id_dsa');
|
||||
var CLIENT_KEY_DSA = fs.readFileSync(CLIENT_KEY_DSA_PATH);
|
||||
var CLIENT_KEY_DSA_PUB = utils.genPublicKey(utils.parseKey(CLIENT_KEY_DSA));
|
||||
if (semver.gte(process.version, '5.2.0')) {
|
||||
var CLIENT_KEY_ECDSA_PATH = join(fixturesdir, 'id_ecdsa');
|
||||
var CLIENT_KEY_ECDSA = fs.readFileSync(CLIENT_KEY_ECDSA_PATH);
|
||||
var CLIENT_KEY_ECDSA_PUB = utils.genPublicKey(
|
||||
utils.parseKey(CLIENT_KEY_ECDSA)
|
||||
);
|
||||
}
|
||||
var opensshVer;
|
||||
var DEBUG = false;
|
||||
|
||||
// Fix file modes to avoid OpenSSH client complaints about keys' permissions
|
||||
fs.readdirSync(fixturesdir).forEach(function(file) {
|
||||
fs.chmodSync(join(fixturesdir, file), '0600');
|
||||
});
|
||||
|
||||
var tests = [
|
||||
{ run: function() {
|
||||
var what = this.what;
|
||||
var server;
|
||||
|
||||
server = setup(
|
||||
this,
|
||||
{ privateKeyPath: CLIENT_KEY_RSA_PATH },
|
||||
{ hostKeys: [HOST_KEY_RSA] }
|
||||
);
|
||||
|
||||
server.on('connection', function(conn) {
|
||||
conn.on('authentication', function(ctx) {
|
||||
if (ctx.method === 'none')
|
||||
return ctx.reject();
|
||||
assert(ctx.method === 'publickey',
|
||||
makeMsg(what, 'Unexpected auth method: ' + ctx.method));
|
||||
assert(ctx.username === USER,
|
||||
makeMsg(what, 'Unexpected username: ' + ctx.username));
|
||||
assert(ctx.key.algo === 'ssh-rsa',
|
||||
makeMsg(what, 'Unexpected key algo: ' + ctx.key.algo));
|
||||
assert.deepEqual(CLIENT_KEY_RSA_PUB.public,
|
||||
ctx.key.data,
|
||||
makeMsg(what, 'Public key mismatch'));
|
||||
if (ctx.signature) {
|
||||
var verifier = crypto.createVerify('RSA-SHA1');
|
||||
var pem = CLIENT_KEY_RSA_PUB.publicOrig;
|
||||
verifier.update(ctx.blob);
|
||||
assert(verifier.verify(pem, ctx.signature),
|
||||
makeMsg(what, 'Could not verify PK signature'));
|
||||
ctx.accept();
|
||||
} else
|
||||
ctx.accept();
|
||||
}).on('ready', function() {
|
||||
conn.on('session', function(accept, reject) {
|
||||
var session = accept();
|
||||
if (session) {
|
||||
session.on('exec', function(accept, reject) {
|
||||
var stream = accept();
|
||||
if (stream) {
|
||||
stream.exit(0);
|
||||
stream.end();
|
||||
}
|
||||
}).on('pty', function(accept, reject) {
|
||||
accept && accept();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
what: 'Authenticate with an RSA key'
|
||||
},
|
||||
{ run: function() {
|
||||
var what = this.what;
|
||||
var server;
|
||||
|
||||
server = setup(
|
||||
this,
|
||||
{ privateKeyPath: CLIENT_KEY_DSA_PATH },
|
||||
{ hostKeys: [HOST_KEY_RSA] }
|
||||
);
|
||||
|
||||
server.on('connection', function(conn) {
|
||||
conn.on('authentication', function(ctx) {
|
||||
if (ctx.method === 'none')
|
||||
return ctx.reject();
|
||||
assert(ctx.method === 'publickey',
|
||||
makeMsg(what, 'Unexpected auth method: ' + ctx.method));
|
||||
assert(ctx.username === USER,
|
||||
makeMsg(what, 'Unexpected username: ' + ctx.username));
|
||||
assert(ctx.key.algo === 'ssh-dss',
|
||||
makeMsg(what, 'Unexpected key algo: ' + ctx.key.algo));
|
||||
assert.deepEqual(CLIENT_KEY_DSA_PUB.public,
|
||||
ctx.key.data,
|
||||
makeMsg(what, 'Public key mismatch'));
|
||||
if (ctx.signature) {
|
||||
var verifier = crypto.createVerify('DSA-SHA1');
|
||||
var pem = CLIENT_KEY_DSA_PUB.publicOrig;
|
||||
verifier.update(ctx.blob);
|
||||
assert(verifier.verify(pem, ctx.signature),
|
||||
makeMsg(what, 'Could not verify PK signature'));
|
||||
ctx.accept();
|
||||
} else
|
||||
ctx.accept();
|
||||
}).on('ready', function() {
|
||||
conn.on('session', function(accept, reject) {
|
||||
var session = accept();
|
||||
if (session) {
|
||||
session.on('exec', function(accept, reject) {
|
||||
var stream = accept();
|
||||
if (stream) {
|
||||
stream.exit(0);
|
||||
stream.end();
|
||||
}
|
||||
}).on('pty', function(accept, reject) {
|
||||
accept && accept();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
what: 'Authenticate with a DSA key'
|
||||
},
|
||||
{ run: function() {
|
||||
if (semver.lt(process.version, '5.2.0'))
|
||||
return next();
|
||||
var what = this.what;
|
||||
var server;
|
||||
|
||||
server = setup(
|
||||
this,
|
||||
{ privateKeyPath: CLIENT_KEY_ECDSA_PATH },
|
||||
{ hostKeys: [HOST_KEY_RSA] }
|
||||
);
|
||||
|
||||
server.on('connection', function(conn) {
|
||||
conn.on('authentication', function(ctx) {
|
||||
if (ctx.method === 'none')
|
||||
return ctx.reject();
|
||||
assert(ctx.method === 'publickey',
|
||||
makeMsg(what, 'Unexpected auth method: ' + ctx.method));
|
||||
assert(ctx.username === USER,
|
||||
makeMsg(what, 'Unexpected username: ' + ctx.username));
|
||||
assert(ctx.key.algo === 'ecdsa-sha2-nistp256',
|
||||
makeMsg(what, 'Unexpected key algo: ' + ctx.key.algo));
|
||||
assert.deepEqual(CLIENT_KEY_ECDSA_PUB.public,
|
||||
ctx.key.data,
|
||||
makeMsg(what, 'Public key mismatch'));
|
||||
if (ctx.signature) {
|
||||
var verifier = crypto.createVerify('sha256');
|
||||
var pem = CLIENT_KEY_ECDSA_PUB.publicOrig;
|
||||
verifier.update(ctx.blob);
|
||||
assert(verifier.verify(pem, ctx.signature),
|
||||
makeMsg(what, 'Could not verify PK signature'));
|
||||
ctx.accept();
|
||||
} else
|
||||
ctx.accept();
|
||||
}).on('ready', function() {
|
||||
conn.on('session', function(accept, reject) {
|
||||
var session = accept();
|
||||
if (session) {
|
||||
session.on('exec', function(accept, reject) {
|
||||
var stream = accept();
|
||||
if (stream) {
|
||||
stream.exit(0);
|
||||
stream.end();
|
||||
}
|
||||
}).on('pty', function(accept, reject) {
|
||||
accept && accept();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
what: 'Authenticate with a ECDSA key'
|
||||
},
|
||||
{ run: function() {
|
||||
var server;
|
||||
|
||||
server = setup(
|
||||
this,
|
||||
{ privateKeyPath: CLIENT_KEY_RSA_PATH },
|
||||
{ hostKeys: [HOST_KEY_DSA] }
|
||||
);
|
||||
|
||||
server.on('connection', function(conn) {
|
||||
conn.on('authentication', function(ctx) {
|
||||
ctx.accept();
|
||||
}).on('ready', function() {
|
||||
conn.on('session', function(accept, reject) {
|
||||
var session = accept();
|
||||
if (session) {
|
||||
session.on('exec', function(accept, reject) {
|
||||
var stream = accept();
|
||||
if (stream) {
|
||||
stream.exit(0);
|
||||
stream.end();
|
||||
}
|
||||
}).on('pty', function(accept, reject) {
|
||||
accept && accept();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
what: 'Server with DSA host key'
|
||||
},
|
||||
{ run: function() {
|
||||
if (semver.lt(process.version, '5.2.0'))
|
||||
return next();
|
||||
var server;
|
||||
|
||||
server = setup(
|
||||
this,
|
||||
{ privateKeyPath: CLIENT_KEY_RSA_PATH },
|
||||
{ hostKeys: [HOST_KEY_ECDSA] }
|
||||
);
|
||||
|
||||
server.on('connection', function(conn) {
|
||||
conn.on('authentication', function(ctx) {
|
||||
ctx.accept();
|
||||
}).on('ready', function() {
|
||||
conn.on('session', function(accept, reject) {
|
||||
var session = accept();
|
||||
if (session) {
|
||||
session.on('exec', function(accept, reject) {
|
||||
var stream = accept();
|
||||
if (stream) {
|
||||
stream.exit(0);
|
||||
stream.end();
|
||||
}
|
||||
}).on('pty', function(accept, reject) {
|
||||
accept && accept();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
what: 'Server with ECDSA host key'
|
||||
},
|
||||
{ run: function() {
|
||||
var server;
|
||||
var what = this.what;
|
||||
|
||||
server = setup(
|
||||
this,
|
||||
{ privateKeyPath: CLIENT_KEY_RSA_PATH },
|
||||
{ hostKeys: [HOST_KEY_RSA] }
|
||||
);
|
||||
|
||||
server.on('_child', function(childProc) {
|
||||
childProc.stderr.once('data', function(data) {
|
||||
childProc.stdin.end();
|
||||
});
|
||||
childProc.stdin.write('ping');
|
||||
}).on('connection', function(conn) {
|
||||
conn.on('authentication', function(ctx) {
|
||||
ctx.accept();
|
||||
}).on('ready', function() {
|
||||
conn.on('session', function(accept, reject) {
|
||||
var session = accept();
|
||||
assert(session, makeMsg(what, 'Missing session'));
|
||||
session.on('exec', function(accept, reject) {
|
||||
var stream = accept();
|
||||
assert(stream, makeMsg(what, 'Missing exec stream'));
|
||||
stream.stdin.on('data', function(data) {
|
||||
stream.stdout.write('pong on stdout');
|
||||
stream.stderr.write('pong on stderr');
|
||||
}).on('end', function() {
|
||||
stream.stdout.write('pong on stdout');
|
||||
stream.stderr.write('pong on stderr');
|
||||
stream.exit(0);
|
||||
stream.close();
|
||||
});
|
||||
}).on('pty', function(accept, reject) {
|
||||
accept && accept();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
what: 'Server closes stdin too early'
|
||||
},
|
||||
];
|
||||
|
||||
function setup(self, clientcfg, servercfg) {
|
||||
self.state = {
|
||||
serverReady: false,
|
||||
clientClose: false,
|
||||
serverClose: false
|
||||
};
|
||||
|
||||
var client;
|
||||
var server = new Server(servercfg);
|
||||
|
||||
server.on('error', onError)
|
||||
.on('connection', function(conn) {
|
||||
conn.on('error', onError)
|
||||
.on('ready', onReady);
|
||||
server.close();
|
||||
})
|
||||
.on('close', onClose);
|
||||
|
||||
function onError(err) {
|
||||
var which = (arguments.length >= 3 ? 'client' : 'server');
|
||||
assert(false, makeMsg(self.what, 'Unexpected ' + which + ' error: ' + err));
|
||||
}
|
||||
function onReady() {
|
||||
assert(!self.state.serverReady,
|
||||
makeMsg(self.what, 'Received multiple ready events for server'));
|
||||
self.state.serverReady = true;
|
||||
self.onReady && self.onReady();
|
||||
}
|
||||
function onClose() {
|
||||
if (arguments.length >= 3) {
|
||||
assert(!self.state.clientClose,
|
||||
makeMsg(self.what, 'Received multiple close events for client'));
|
||||
self.state.clientClose = true;
|
||||
} else {
|
||||
assert(!self.state.serverClose,
|
||||
makeMsg(self.what, 'Received multiple close events for server'));
|
||||
self.state.serverClose = true;
|
||||
}
|
||||
if (self.state.clientClose && self.state.serverClose)
|
||||
next();
|
||||
}
|
||||
|
||||
process.nextTick(function() {
|
||||
server.listen(0, 'localhost', function() {
|
||||
var cmd = 'ssh';
|
||||
var args = ['-o', 'UserKnownHostsFile=/dev/null',
|
||||
'-o', 'StrictHostKeyChecking=no',
|
||||
'-o', 'CheckHostIP=no',
|
||||
'-o', 'ConnectTimeout=3',
|
||||
'-o', 'GlobalKnownHostsFile=/dev/null',
|
||||
'-o', 'GSSAPIAuthentication=no',
|
||||
'-o', 'IdentitiesOnly=yes',
|
||||
'-o', 'BatchMode=yes',
|
||||
'-o', 'VerifyHostKeyDNS=no',
|
||||
|
||||
'-vvvvvv',
|
||||
'-T',
|
||||
'-o', 'KbdInteractiveAuthentication=no',
|
||||
'-o', 'HostbasedAuthentication=no',
|
||||
'-o', 'PasswordAuthentication=no',
|
||||
'-o', 'PubkeyAuthentication=yes',
|
||||
'-o', 'PreferredAuthentications=publickey'];
|
||||
if (clientcfg.privateKeyPath)
|
||||
args.push('-o', 'IdentityFile=' + clientcfg.privateKeyPath);
|
||||
if (!/^[0-6]\./.test(opensshVer)) {
|
||||
// OpenSSH 7.0+ disables DSS/DSA host (and user) key support by
|
||||
// default, so we explicitly enable it here
|
||||
args.push('-o', 'HostKeyAlgorithms=+ssh-dss');
|
||||
}
|
||||
args.push('-p', server.address().port.toString(),
|
||||
'-l', USER,
|
||||
'localhost',
|
||||
'uptime');
|
||||
|
||||
client = spawn(cmd, args);
|
||||
server.emit('_child', client);
|
||||
if (DEBUG) {
|
||||
client.stdout.pipe(process.stdout);
|
||||
client.stderr.pipe(process.stderr);
|
||||
} else {
|
||||
client.stdout.resume();
|
||||
client.stderr.resume();
|
||||
}
|
||||
client.on('error', function(err) {
|
||||
onError(err, null, null);
|
||||
}).on('exit', function(code) {
|
||||
clearTimeout(client.timer);
|
||||
if (code !== 0)
|
||||
return onError(new Error('Non-zero exit code ' + code), null, null);
|
||||
onClose(null, null, null);
|
||||
});
|
||||
|
||||
client.timer = setTimeout(function() {
|
||||
assert(false, makeMsg(self.what, 'Client timeout'));
|
||||
}, CLIENT_TIMEOUT);
|
||||
});
|
||||
});
|
||||
return server;
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (Array.isArray(process._events.exit))
|
||||
process._events.exit = process._events.exit[1];
|
||||
if (++t === tests.length)
|
||||
return;
|
||||
|
||||
var v = tests[t];
|
||||
v.run.call(v);
|
||||
}
|
||||
|
||||
function makeMsg(what, msg) {
|
||||
return '[' + group + what + ']: ' + msg;
|
||||
}
|
||||
|
||||
process.once('uncaughtException', function(err) {
|
||||
if (t > -1 && !/(?:^|\n)AssertionError: /i.test(''+err))
|
||||
console.log(makeMsg(tests[t].what, 'Unexpected Exception:'));
|
||||
throw err;
|
||||
});
|
||||
process.once('exit', function() {
|
||||
assert(t === tests.length,
|
||||
makeMsg('_exit',
|
||||
'Only finished ' + t + '/' + tests.length + ' tests'));
|
||||
});
|
||||
|
||||
|
||||
// Get OpenSSH client version first
|
||||
exec('ssh -V', function(err, stdout, stderr) {
|
||||
if (err) {
|
||||
console.log('OpenSSH client is required for these tests');
|
||||
process.exitCode = 5;
|
||||
return;
|
||||
}
|
||||
var re = /^OpenSSH_([\d\.]+)/;
|
||||
var m = re.exec(stdout.toString());
|
||||
if (!m || !m[1]) {
|
||||
m = re.exec(stderr.toString());
|
||||
if (!m || !m[1]) {
|
||||
console.log('OpenSSH client is required for these tests');
|
||||
process.exitCode = 5;
|
||||
return;
|
||||
}
|
||||
}
|
||||
opensshVer = m[1];
|
||||
next();
|
||||
});
|
||||
Reference in New Issue
Block a user