Files
bare-operating-system/packages/bare-os-openssh/vendor/bare-ssh2/test/test-userauth.js
T

672 lines
18 KiB
JavaScript

'use strict'
const assert = require('./assert-compat.js')
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()
})
)
})
)
}