Initial commit
This commit is contained in:
611
server/node_modules/ssh2/test/test-userauth.js
generated
vendored
Normal file
611
server/node_modules/ssh2/test/test-userauth.js
generated
vendored
Normal file
@@ -0,0 +1,611 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const { inspect } = require('util');
|
||||
|
||||
const {
|
||||
fixtureKey,
|
||||
mustCall,
|
||||
mustNotCall,
|
||||
setup,
|
||||
} = require('./common.js');
|
||||
|
||||
const serverCfg = { hostKeys: [ fixtureKey('ssh_host_rsa_key').raw ] };
|
||||
|
||||
const debug = false;
|
||||
|
||||
// Keys ========================================================================
|
||||
[
|
||||
{ desc: 'RSA (old OpenSSH)',
|
||||
clientKey: fixtureKey('id_rsa') },
|
||||
{ desc: 'RSA (new OpenSSH)',
|
||||
clientKey: fixtureKey('openssh_new_rsa') },
|
||||
{ desc: 'RSA (encrypted)',
|
||||
clientKey: fixtureKey('id_rsa_enc', 'foobarbaz'),
|
||||
passphrase: 'foobarbaz' },
|
||||
{ desc: 'DSA',
|
||||
clientKey: fixtureKey('id_dsa') },
|
||||
{ desc: 'ECDSA',
|
||||
clientKey: fixtureKey('id_ecdsa') },
|
||||
{ desc: 'PPK',
|
||||
clientKey: fixtureKey('id_rsa.ppk') },
|
||||
].forEach((test) => {
|
||||
const { desc, clientKey, passphrase } = test;
|
||||
const username = 'Key User';
|
||||
const { server } = setup(
|
||||
desc,
|
||||
{
|
||||
client: { username, privateKey: clientKey.raw, passphrase },
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
let authAttempt = 0;
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(ctx.username === username,
|
||||
`Wrong username: ${ctx.username}`);
|
||||
switch (++authAttempt) {
|
||||
case 1:
|
||||
assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`);
|
||||
return ctx.reject();
|
||||
case 3:
|
||||
assert(ctx.signature, 'Missing publickey signature');
|
||||
// FALLTHROUGH
|
||||
case 2:
|
||||
assert(ctx.method === 'publickey',
|
||||
`Wrong auth method: ${ctx.method}`);
|
||||
assert(ctx.key.algo === clientKey.key.type,
|
||||
`Wrong key algo: ${ctx.key.algo}`);
|
||||
assert.deepStrictEqual(clientKey.key.getPublicSSH(),
|
||||
ctx.key.data,
|
||||
'Public key mismatch');
|
||||
break;
|
||||
}
|
||||
if (ctx.signature) {
|
||||
const result =
|
||||
clientKey.key.verify(ctx.blob, ctx.signature, ctx.hashAlgo);
|
||||
assert(result === true, 'Could not verify publickey signature');
|
||||
}
|
||||
ctx.accept();
|
||||
}, 3)).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
// Password ====================================================================
|
||||
{
|
||||
const username = 'Password User';
|
||||
const password = 'hi mom';
|
||||
const { server } = setup(
|
||||
'Password',
|
||||
{
|
||||
client: { username, password },
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
let authAttempt = 0;
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(ctx.username === username,
|
||||
`Wrong username: ${ctx.username}`);
|
||||
if (++authAttempt === 1) {
|
||||
assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`);
|
||||
return ctx.reject();
|
||||
}
|
||||
assert(ctx.method === 'password',
|
||||
`Wrong auth method: ${ctx.method}`);
|
||||
assert(ctx.password === password,
|
||||
`Wrong password: ${ctx.password}`);
|
||||
ctx.accept();
|
||||
}, 2)).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
}
|
||||
{
|
||||
const username = '';
|
||||
const password = 'hi mom';
|
||||
const { server } = setup(
|
||||
'Password (empty username)',
|
||||
{
|
||||
client: { username, password },
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
let authAttempt = 0;
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(ctx.username === username,
|
||||
`Wrong username: ${ctx.username}`);
|
||||
if (++authAttempt === 1) {
|
||||
assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`);
|
||||
return ctx.reject();
|
||||
}
|
||||
assert(ctx.method === 'password',
|
||||
`Wrong auth method: ${ctx.method}`);
|
||||
assert(ctx.password === password,
|
||||
`Wrong password: ${ctx.password}`);
|
||||
ctx.accept();
|
||||
}, 2)).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
}
|
||||
{
|
||||
const username = 'foo';
|
||||
const oldPassword = 'bar';
|
||||
const newPassword = 'baz';
|
||||
const changePrompt = 'Prithee changeth thy password';
|
||||
const { client, server } = setup(
|
||||
'Password (change requested)',
|
||||
{
|
||||
client: { username, password: oldPassword },
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
let authAttempt = 0;
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(ctx.username === username,
|
||||
`Wrong username: ${ctx.username}`);
|
||||
if (++authAttempt === 1) {
|
||||
assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`);
|
||||
return ctx.reject();
|
||||
}
|
||||
assert(ctx.method === 'password',
|
||||
`Wrong auth method: ${ctx.method}`);
|
||||
assert(ctx.password === oldPassword,
|
||||
`Wrong old password: ${ctx.password}`);
|
||||
ctx.requestChange(changePrompt, mustCall((newPassword_) => {
|
||||
assert(newPassword_ === newPassword,
|
||||
`Wrong new password: ${newPassword_}`);
|
||||
ctx.accept();
|
||||
}));
|
||||
}, 2)).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
|
||||
client.on('change password', mustCall((prompt, done) => {
|
||||
assert(prompt === changePrompt, `Wrong password change prompt: ${prompt}`);
|
||||
process.nextTick(done, newPassword);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
// Hostbased ===================================================================
|
||||
{
|
||||
const localUsername = 'Local User Foo';
|
||||
const localHostname = 'Local Host Bar';
|
||||
const username = 'Hostbased User';
|
||||
const clientKey = fixtureKey('id_rsa');
|
||||
const { server } = setup(
|
||||
'Hostbased',
|
||||
{
|
||||
client: {
|
||||
username,
|
||||
privateKey: clientKey.raw,
|
||||
localUsername,
|
||||
localHostname,
|
||||
},
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
let authAttempt = 0;
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(ctx.username === username,
|
||||
`Wrong username: ${ctx.username}`);
|
||||
switch (++authAttempt) {
|
||||
case 1:
|
||||
assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`);
|
||||
return ctx.reject();
|
||||
case 2:
|
||||
assert(ctx.method === 'publickey',
|
||||
`Wrong auth method: ${ctx.method}`);
|
||||
return ctx.reject();
|
||||
case 3: {
|
||||
assert(ctx.method === 'hostbased',
|
||||
`Wrong auth method: ${ctx.method}`);
|
||||
assert(ctx.key.algo === clientKey.key.type,
|
||||
`Wrong key algo: ${ctx.key.algo}`);
|
||||
assert.deepStrictEqual(clientKey.key.getPublicSSH(),
|
||||
ctx.key.data,
|
||||
'Public key mismatch');
|
||||
assert(ctx.signature, 'Expected signature');
|
||||
assert(ctx.localHostname === localHostname, 'Wrong local hostname');
|
||||
assert(ctx.localUsername === localUsername, 'Wrong local username');
|
||||
const result =
|
||||
clientKey.key.verify(ctx.blob, ctx.signature, ctx.hashAlgo);
|
||||
assert(result === true, 'Could not verify hostbased signature');
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx.accept();
|
||||
}, 3)).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
// keyboard-interactive ========================================================
|
||||
{
|
||||
const username = 'Keyboard-Interactive User';
|
||||
const request = {
|
||||
name: 'SSH2 Authentication',
|
||||
instructions: 'These are instructions',
|
||||
prompts: [
|
||||
{ prompt: 'Password: ', echo: false },
|
||||
{ prompt: 'Is the cake a lie? ', echo: true },
|
||||
],
|
||||
};
|
||||
const responses = [
|
||||
'foobarbaz',
|
||||
'yes',
|
||||
];
|
||||
const { client, server } = setup(
|
||||
'Password (empty username)',
|
||||
{
|
||||
client: {
|
||||
username,
|
||||
tryKeyboard: true,
|
||||
},
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
let authAttempt = 0;
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(ctx.username === username,
|
||||
`Wrong username: ${ctx.username}`);
|
||||
if (++authAttempt === 1) {
|
||||
assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`);
|
||||
return ctx.reject();
|
||||
}
|
||||
assert(ctx.method === 'keyboard-interactive',
|
||||
`Wrong auth method: ${ctx.method}`);
|
||||
ctx.prompt(request.prompts,
|
||||
request.name,
|
||||
request.instructions,
|
||||
mustCall((responses_) => {
|
||||
assert.deepStrictEqual(responses_, responses);
|
||||
ctx.accept();
|
||||
}));
|
||||
}, 2)).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
|
||||
client.on('keyboard-interactive',
|
||||
mustCall((name, instructions, lang, prompts, finish) => {
|
||||
assert(name === request.name, `Wrong prompt name: ${name}`);
|
||||
assert(instructions === request.instructions,
|
||||
`Wrong prompt instructions: ${instructions}`);
|
||||
assert.deepStrictEqual(
|
||||
prompts,
|
||||
request.prompts,
|
||||
`Wrong prompts: ${inspect(prompts)}`
|
||||
);
|
||||
process.nextTick(finish, responses);
|
||||
}));
|
||||
}
|
||||
|
||||
// authHandler() tests =========================================================
|
||||
{
|
||||
const username = 'foo';
|
||||
const password = '1234';
|
||||
const clientKey = fixtureKey('id_rsa');
|
||||
const { server } = setup(
|
||||
'authHandler() (sync)',
|
||||
{
|
||||
client: {
|
||||
username,
|
||||
password,
|
||||
privateKey: clientKey.raw,
|
||||
|
||||
authHandler: mustCall((methodsLeft, partial, cb) => {
|
||||
assert(methodsLeft === null, 'expected null methodsLeft');
|
||||
assert(partial === null, 'expected null partial');
|
||||
return 'none';
|
||||
}),
|
||||
},
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(ctx.username === username, `Wrong username: ${ctx.username}`);
|
||||
assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`);
|
||||
ctx.accept();
|
||||
})).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
}
|
||||
{
|
||||
const username = 'foo';
|
||||
const password = '1234';
|
||||
const clientKey = fixtureKey('id_rsa');
|
||||
const { server } = setup(
|
||||
'authHandler() (async)',
|
||||
{
|
||||
client: {
|
||||
username,
|
||||
password,
|
||||
privateKey: clientKey.raw,
|
||||
|
||||
authHandler: mustCall((methodsLeft, partial, cb) => {
|
||||
assert(methodsLeft === null, 'expected null methodsLeft');
|
||||
assert(partial === null, 'expected null partial');
|
||||
process.nextTick(mustCall(cb), 'none');
|
||||
}),
|
||||
},
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(ctx.username === username, `Wrong username: ${ctx.username}`);
|
||||
assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`);
|
||||
ctx.accept();
|
||||
})).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
}
|
||||
{
|
||||
const username = 'foo';
|
||||
const password = '1234';
|
||||
const clientKey = fixtureKey('id_rsa');
|
||||
const { client, server } = setup(
|
||||
'authHandler() (no methods left -- sync)',
|
||||
{
|
||||
client: {
|
||||
username,
|
||||
password,
|
||||
privateKey: clientKey.raw,
|
||||
|
||||
authHandler: mustCall((methodsLeft, partial, cb) => {
|
||||
assert(methodsLeft === null, 'expected null methodsLeft');
|
||||
assert(partial === null, 'expected null partial');
|
||||
return false;
|
||||
}),
|
||||
},
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
noForceClientReady: true,
|
||||
noForceServerReady: true,
|
||||
}
|
||||
);
|
||||
|
||||
// Remove default client error handler added by `setup()` since we are
|
||||
// expecting an error in this case
|
||||
client.removeAllListeners('error');
|
||||
|
||||
client.on('error', mustCall((err) => {
|
||||
assert.strictEqual(err.level, 'client-authentication');
|
||||
assert(/configured authentication methods failed/i.test(err.message),
|
||||
'Wrong error message');
|
||||
}));
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
conn.on('authentication', mustNotCall())
|
||||
.on('ready', mustNotCall());
|
||||
}));
|
||||
}
|
||||
{
|
||||
const username = 'foo';
|
||||
const password = '1234';
|
||||
const clientKey = fixtureKey('id_rsa');
|
||||
const { client, server } = setup(
|
||||
'authHandler() (no methods left -- async)',
|
||||
{
|
||||
client: {
|
||||
username,
|
||||
password,
|
||||
privateKey: clientKey.raw,
|
||||
|
||||
authHandler: mustCall((methodsLeft, partial, cb) => {
|
||||
assert(methodsLeft === null, 'expected null methodsLeft');
|
||||
assert(partial === null, 'expected null partial');
|
||||
process.nextTick(mustCall(cb), false);
|
||||
}),
|
||||
},
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
noForceClientReady: true,
|
||||
noForceServerReady: true,
|
||||
}
|
||||
);
|
||||
|
||||
// Remove default client error handler added by `setup()` since we are
|
||||
// expecting an error in this case
|
||||
client.removeAllListeners('error');
|
||||
|
||||
client.on('error', mustCall((err) => {
|
||||
assert.strictEqual(err.level, 'client-authentication');
|
||||
assert(/configured authentication methods failed/i.test(err.message),
|
||||
'Wrong error message');
|
||||
}));
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
conn.on('authentication', mustNotCall())
|
||||
.on('ready', mustNotCall());
|
||||
}));
|
||||
}
|
||||
{
|
||||
const username = 'foo';
|
||||
const password = '1234';
|
||||
const clientKey = fixtureKey('id_rsa');
|
||||
const events = [];
|
||||
const expectedEvents = [
|
||||
'client', 'server', 'client', 'server'
|
||||
];
|
||||
let clientCalls = 0;
|
||||
const { client, server } = setup(
|
||||
'authHandler() (multi-step)',
|
||||
{
|
||||
client: {
|
||||
username,
|
||||
password,
|
||||
privateKey: clientKey.raw,
|
||||
|
||||
authHandler: mustCall((methodsLeft, partial, cb) => {
|
||||
events.push('client');
|
||||
switch (++clientCalls) {
|
||||
case 1:
|
||||
assert(methodsLeft === null, 'expected null methodsLeft');
|
||||
assert(partial === null, 'expected null partial');
|
||||
return 'publickey';
|
||||
case 2:
|
||||
assert.deepStrictEqual(
|
||||
methodsLeft,
|
||||
['password'],
|
||||
`expected 'password' method left, saw: ${methodsLeft}`
|
||||
);
|
||||
assert(partial === true, 'expected partial success');
|
||||
return 'password';
|
||||
}
|
||||
}, 2),
|
||||
},
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
let attempts = 0;
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(++attempts === clientCalls, 'server<->client state mismatch');
|
||||
assert(ctx.username === username,
|
||||
`Unexpected username: ${ctx.username}`);
|
||||
events.push('server');
|
||||
switch (attempts) {
|
||||
case 1:
|
||||
assert(ctx.method === 'publickey',
|
||||
`Wrong auth method: ${ctx.method}`);
|
||||
assert(ctx.key.algo === clientKey.key.type,
|
||||
`Unexpected key algo: ${ctx.key.algo}`);
|
||||
assert.deepEqual(clientKey.key.getPublicSSH(),
|
||||
ctx.key.data,
|
||||
'Public key mismatch');
|
||||
ctx.reject(['password'], true);
|
||||
break;
|
||||
case 2:
|
||||
assert(ctx.method === 'password',
|
||||
`Wrong auth method: ${ctx.method}`);
|
||||
assert(ctx.password === password,
|
||||
`Unexpected password: ${ctx.password}`);
|
||||
ctx.accept();
|
||||
break;
|
||||
}
|
||||
}, 2)).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
|
||||
client.on('close', mustCall(() => {
|
||||
assert.deepStrictEqual(events, expectedEvents);
|
||||
}));
|
||||
}
|
||||
{
|
||||
const username = 'foo';
|
||||
const password = '1234';
|
||||
const { server } = setup(
|
||||
'authHandler() (custom auth configuration)',
|
||||
{
|
||||
client: {
|
||||
username: 'bar',
|
||||
password: '5678',
|
||||
|
||||
authHandler: mustCall((methodsLeft, partial, cb) => {
|
||||
assert(methodsLeft === null, 'expected null methodsLeft');
|
||||
assert(partial === null, 'expected null partial');
|
||||
return {
|
||||
type: 'password',
|
||||
username,
|
||||
password,
|
||||
};
|
||||
}),
|
||||
},
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(ctx.username === username, `Wrong username: ${ctx.username}`);
|
||||
assert(ctx.method === 'password', `Wrong auth method: ${ctx.method}`);
|
||||
assert(ctx.password === password, `Unexpected password: ${ctx.password}`);
|
||||
ctx.accept();
|
||||
})).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
}
|
||||
{
|
||||
const username = 'foo';
|
||||
const password = '1234';
|
||||
const { server } = setup(
|
||||
'authHandler() (simple construction with custom auth configuration)',
|
||||
{
|
||||
client: {
|
||||
username: 'bar',
|
||||
password: '5678',
|
||||
|
||||
authHandler: [{
|
||||
type: 'password',
|
||||
username,
|
||||
password,
|
||||
}],
|
||||
},
|
||||
server: serverCfg,
|
||||
|
||||
debug,
|
||||
}
|
||||
);
|
||||
|
||||
server.on('connection', mustCall((conn) => {
|
||||
conn.on('authentication', mustCall((ctx) => {
|
||||
assert(ctx.username === username, `Wrong username: ${ctx.username}`);
|
||||
assert(ctx.method === 'password', `Wrong auth method: ${ctx.method}`);
|
||||
assert(ctx.password === password, `Unexpected password: ${ctx.password}`);
|
||||
ctx.accept();
|
||||
})).on('ready', mustCall(() => {
|
||||
conn.end();
|
||||
}));
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user