mirror of
https://github.com/NuSkooler/enigma-bbs.git
synced 2025-06-11 07:04:32 +02:00
ENiGMA 1/2 WILL USE SPACES FROM THIS POINT ON VS TABS
* Really just to make GitHub formatting happy. Arg.
This commit is contained in:
parent
5ddf04c882
commit
e9787cee3e
135 changed files with 27397 additions and 27397 deletions
|
@ -19,215 +19,215 @@ const _ = require('lodash');
|
|||
const assert = require('assert');
|
||||
|
||||
const ModuleInfo = exports.moduleInfo = {
|
||||
name : 'SSH',
|
||||
desc : 'SSH Server',
|
||||
author : 'NuSkooler',
|
||||
isSecure : true,
|
||||
packageName : 'codes.l33t.enigma.ssh.server',
|
||||
name : 'SSH',
|
||||
desc : 'SSH Server',
|
||||
author : 'NuSkooler',
|
||||
isSecure : true,
|
||||
packageName : 'codes.l33t.enigma.ssh.server',
|
||||
};
|
||||
|
||||
function SSHClient(clientConn) {
|
||||
baseClient.Client.apply(this, arguments);
|
||||
baseClient.Client.apply(this, arguments);
|
||||
|
||||
//
|
||||
// WARNING: Until we have emit 'ready', self.input, and self.output and
|
||||
// not yet defined!
|
||||
//
|
||||
//
|
||||
// WARNING: Until we have emit 'ready', self.input, and self.output and
|
||||
// not yet defined!
|
||||
//
|
||||
|
||||
const self = this;
|
||||
const self = this;
|
||||
|
||||
let loginAttempts = 0;
|
||||
let loginAttempts = 0;
|
||||
|
||||
clientConn.on('authentication', function authAttempt(ctx) {
|
||||
const username = ctx.username || '';
|
||||
const password = ctx.password || '';
|
||||
clientConn.on('authentication', function authAttempt(ctx) {
|
||||
const username = ctx.username || '';
|
||||
const password = ctx.password || '';
|
||||
|
||||
const config = Config();
|
||||
self.isNewUser = (config.users.newUserNames || []).indexOf(username) > -1;
|
||||
const config = Config();
|
||||
self.isNewUser = (config.users.newUserNames || []).indexOf(username) > -1;
|
||||
|
||||
self.log.trace( { method : ctx.method, username : username, newUser : self.isNewUser }, 'SSH authentication attempt');
|
||||
self.log.trace( { method : ctx.method, username : username, newUser : self.isNewUser }, 'SSH authentication attempt');
|
||||
|
||||
function terminateConnection() {
|
||||
ctx.reject();
|
||||
return clientConn.end();
|
||||
}
|
||||
function terminateConnection() {
|
||||
ctx.reject();
|
||||
return clientConn.end();
|
||||
}
|
||||
|
||||
function alreadyLoggedIn(username) {
|
||||
ctx.prompt(`${username} is already connected to the system. Terminating connection.\n(Press any key to continue)`);
|
||||
return terminateConnection();
|
||||
}
|
||||
function alreadyLoggedIn(username) {
|
||||
ctx.prompt(`${username} is already connected to the system. Terminating connection.\n(Press any key to continue)`);
|
||||
return terminateConnection();
|
||||
}
|
||||
|
||||
//
|
||||
// If the system is open and |isNewUser| is true, the login
|
||||
// sequence is hijacked in order to start the applicaiton process.
|
||||
//
|
||||
if(false === config.general.closedSystem && self.isNewUser) {
|
||||
return ctx.accept();
|
||||
}
|
||||
//
|
||||
// If the system is open and |isNewUser| is true, the login
|
||||
// sequence is hijacked in order to start the applicaiton process.
|
||||
//
|
||||
if(false === config.general.closedSystem && self.isNewUser) {
|
||||
return ctx.accept();
|
||||
}
|
||||
|
||||
if(username.length > 0 && password.length > 0) {
|
||||
loginAttempts += 1;
|
||||
if(username.length > 0 && password.length > 0) {
|
||||
loginAttempts += 1;
|
||||
|
||||
userLogin(self, ctx.username, ctx.password, function authResult(err) {
|
||||
if(err) {
|
||||
if(err.existingConn) {
|
||||
return alreadyLoggedIn(username);
|
||||
}
|
||||
userLogin(self, ctx.username, ctx.password, function authResult(err) {
|
||||
if(err) {
|
||||
if(err.existingConn) {
|
||||
return alreadyLoggedIn(username);
|
||||
}
|
||||
|
||||
return ctx.reject(SSHClient.ValidAuthMethods);
|
||||
}
|
||||
return ctx.reject(SSHClient.ValidAuthMethods);
|
||||
}
|
||||
|
||||
ctx.accept();
|
||||
});
|
||||
} else {
|
||||
if(-1 === SSHClient.ValidAuthMethods.indexOf(ctx.method)) {
|
||||
return ctx.reject(SSHClient.ValidAuthMethods);
|
||||
}
|
||||
ctx.accept();
|
||||
});
|
||||
} else {
|
||||
if(-1 === SSHClient.ValidAuthMethods.indexOf(ctx.method)) {
|
||||
return ctx.reject(SSHClient.ValidAuthMethods);
|
||||
}
|
||||
|
||||
if(0 === username.length) {
|
||||
// :TODO: can we display something here?
|
||||
return ctx.reject();
|
||||
}
|
||||
if(0 === username.length) {
|
||||
// :TODO: can we display something here?
|
||||
return ctx.reject();
|
||||
}
|
||||
|
||||
const interactivePrompt = { prompt : `${ctx.username}'s password: `, echo : false };
|
||||
const interactivePrompt = { prompt : `${ctx.username}'s password: `, echo : false };
|
||||
|
||||
ctx.prompt(interactivePrompt, function retryPrompt(answers) {
|
||||
loginAttempts += 1;
|
||||
ctx.prompt(interactivePrompt, function retryPrompt(answers) {
|
||||
loginAttempts += 1;
|
||||
|
||||
userLogin(self, username, (answers[0] || ''), err => {
|
||||
if(err) {
|
||||
if(err.existingConn) {
|
||||
return alreadyLoggedIn(username);
|
||||
}
|
||||
userLogin(self, username, (answers[0] || ''), err => {
|
||||
if(err) {
|
||||
if(err.existingConn) {
|
||||
return alreadyLoggedIn(username);
|
||||
}
|
||||
|
||||
if(loginAttempts >= config.general.loginAttempts) {
|
||||
return terminateConnection();
|
||||
}
|
||||
if(loginAttempts >= config.general.loginAttempts) {
|
||||
return terminateConnection();
|
||||
}
|
||||
|
||||
const artOpts = {
|
||||
client : self,
|
||||
name : 'SSHPMPT.ASC',
|
||||
readSauce : false,
|
||||
};
|
||||
const artOpts = {
|
||||
client : self,
|
||||
name : 'SSHPMPT.ASC',
|
||||
readSauce : false,
|
||||
};
|
||||
|
||||
theme.getThemeArt(artOpts, (err, artInfo) => {
|
||||
if(err) {
|
||||
interactivePrompt.prompt = `Access denied\n${ctx.username}'s password: `;
|
||||
} else {
|
||||
const newUserNameList = _.has(config, 'users.newUserNames') && config.users.newUserNames.length > 0 ?
|
||||
config.users.newUserNames.map(newName => '"' + newName + '"').join(', ') :
|
||||
'(No new user names enabled!)';
|
||||
theme.getThemeArt(artOpts, (err, artInfo) => {
|
||||
if(err) {
|
||||
interactivePrompt.prompt = `Access denied\n${ctx.username}'s password: `;
|
||||
} else {
|
||||
const newUserNameList = _.has(config, 'users.newUserNames') && config.users.newUserNames.length > 0 ?
|
||||
config.users.newUserNames.map(newName => '"' + newName + '"').join(', ') :
|
||||
'(No new user names enabled!)';
|
||||
|
||||
interactivePrompt.prompt = `Access denied\n${stringFormat(artInfo.data, { newUserNames : newUserNameList })}\n${ctx.username}'s password'`;
|
||||
}
|
||||
return ctx.prompt(interactivePrompt, retryPrompt);
|
||||
});
|
||||
} else {
|
||||
ctx.accept();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
interactivePrompt.prompt = `Access denied\n${stringFormat(artInfo.data, { newUserNames : newUserNameList })}\n${ctx.username}'s password'`;
|
||||
}
|
||||
return ctx.prompt(interactivePrompt, retryPrompt);
|
||||
});
|
||||
} else {
|
||||
ctx.accept();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.dataHandler = function(data) {
|
||||
self.emit('data', data);
|
||||
};
|
||||
this.dataHandler = function(data) {
|
||||
self.emit('data', data);
|
||||
};
|
||||
|
||||
this.updateTermInfo = function(info) {
|
||||
//
|
||||
// From ssh2 docs:
|
||||
// "rows and cols override width and height when rows and cols are non-zero."
|
||||
//
|
||||
let termHeight;
|
||||
let termWidth;
|
||||
this.updateTermInfo = function(info) {
|
||||
//
|
||||
// From ssh2 docs:
|
||||
// "rows and cols override width and height when rows and cols are non-zero."
|
||||
//
|
||||
let termHeight;
|
||||
let termWidth;
|
||||
|
||||
if(info.rows > 0 && info.cols > 0) {
|
||||
termHeight = info.rows;
|
||||
termWidth = info.cols;
|
||||
} else if(info.width > 0 && info.height > 0) {
|
||||
termHeight = info.height;
|
||||
termWidth = info.width;
|
||||
}
|
||||
if(info.rows > 0 && info.cols > 0) {
|
||||
termHeight = info.rows;
|
||||
termWidth = info.cols;
|
||||
} else if(info.width > 0 && info.height > 0) {
|
||||
termHeight = info.height;
|
||||
termWidth = info.width;
|
||||
}
|
||||
|
||||
assert(_.isObject(self.term));
|
||||
assert(_.isObject(self.term));
|
||||
|
||||
//
|
||||
// Note that if we fail here, connect.js attempts some non-standard
|
||||
// queries/etc., and ultimately will default to 80x24 if all else fails
|
||||
//
|
||||
if(termHeight > 0 && termWidth > 0) {
|
||||
self.term.termHeight = termHeight;
|
||||
self.term.termWidth = termWidth;
|
||||
//
|
||||
// Note that if we fail here, connect.js attempts some non-standard
|
||||
// queries/etc., and ultimately will default to 80x24 if all else fails
|
||||
//
|
||||
if(termHeight > 0 && termWidth > 0) {
|
||||
self.term.termHeight = termHeight;
|
||||
self.term.termWidth = termWidth;
|
||||
|
||||
self.clearMciCache(); // term size changes = invalidate cache
|
||||
}
|
||||
self.clearMciCache(); // term size changes = invalidate cache
|
||||
}
|
||||
|
||||
if(_.isString(info.term) && info.term.length > 0 && 'unknown' === self.term.termType) {
|
||||
self.setTermType(info.term);
|
||||
}
|
||||
};
|
||||
if(_.isString(info.term) && info.term.length > 0 && 'unknown' === self.term.termType) {
|
||||
self.setTermType(info.term);
|
||||
}
|
||||
};
|
||||
|
||||
clientConn.once('ready', function clientReady() {
|
||||
self.log.info('SSH authentication success');
|
||||
clientConn.once('ready', function clientReady() {
|
||||
self.log.info('SSH authentication success');
|
||||
|
||||
clientConn.on('session', accept => {
|
||||
clientConn.on('session', accept => {
|
||||
|
||||
const session = accept();
|
||||
const session = accept();
|
||||
|
||||
session.on('pty', function pty(accept, reject, info) {
|
||||
self.log.debug(info, 'SSH pty event');
|
||||
session.on('pty', function pty(accept, reject, info) {
|
||||
self.log.debug(info, 'SSH pty event');
|
||||
|
||||
if(_.isFunction(accept)) {
|
||||
accept();
|
||||
}
|
||||
if(_.isFunction(accept)) {
|
||||
accept();
|
||||
}
|
||||
|
||||
if(self.input) { // do we have I/O?
|
||||
self.updateTermInfo(info);
|
||||
} else {
|
||||
self.cachedTermInfo = info;
|
||||
}
|
||||
});
|
||||
if(self.input) { // do we have I/O?
|
||||
self.updateTermInfo(info);
|
||||
} else {
|
||||
self.cachedTermInfo = info;
|
||||
}
|
||||
});
|
||||
|
||||
session.on('shell', accept => {
|
||||
self.log.debug('SSH shell event');
|
||||
session.on('shell', accept => {
|
||||
self.log.debug('SSH shell event');
|
||||
|
||||
const channel = accept();
|
||||
const channel = accept();
|
||||
|
||||
self.setInputOutput(channel.stdin, channel.stdout);
|
||||
self.setInputOutput(channel.stdin, channel.stdout);
|
||||
|
||||
channel.stdin.on('data', self.dataHandler);
|
||||
channel.stdin.on('data', self.dataHandler);
|
||||
|
||||
if(self.cachedTermInfo) {
|
||||
self.updateTermInfo(self.cachedTermInfo);
|
||||
delete self.cachedTermInfo;
|
||||
}
|
||||
if(self.cachedTermInfo) {
|
||||
self.updateTermInfo(self.cachedTermInfo);
|
||||
delete self.cachedTermInfo;
|
||||
}
|
||||
|
||||
// we're ready!
|
||||
const firstMenu = self.isNewUser ? Config().loginServers.ssh.firstMenuNewUser : Config().loginServers.ssh.firstMenu;
|
||||
self.emit('ready', { firstMenu : firstMenu } );
|
||||
});
|
||||
// we're ready!
|
||||
const firstMenu = self.isNewUser ? Config().loginServers.ssh.firstMenuNewUser : Config().loginServers.ssh.firstMenu;
|
||||
self.emit('ready', { firstMenu : firstMenu } );
|
||||
});
|
||||
|
||||
session.on('window-change', (accept, reject, info) => {
|
||||
self.log.debug(info, 'SSH window-change event');
|
||||
session.on('window-change', (accept, reject, info) => {
|
||||
self.log.debug(info, 'SSH window-change event');
|
||||
|
||||
if(self.input) {
|
||||
self.updateTermInfo(info);
|
||||
} else {
|
||||
self.cachedTermInfo = info;
|
||||
}
|
||||
});
|
||||
if(self.input) {
|
||||
self.updateTermInfo(info);
|
||||
} else {
|
||||
self.cachedTermInfo = info;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
clientConn.on('end', () => {
|
||||
self.emit('end'); // remove client connection/tracking
|
||||
});
|
||||
clientConn.on('end', () => {
|
||||
self.emit('end'); // remove client connection/tracking
|
||||
});
|
||||
|
||||
clientConn.on('error', err => {
|
||||
self.log.warn( { error : err.message, code : err.code }, 'SSH connection error');
|
||||
});
|
||||
clientConn.on('error', err => {
|
||||
self.log.warn( { error : err.message, code : err.code }, 'SSH connection error');
|
||||
});
|
||||
}
|
||||
|
||||
util.inherits(SSHClient, baseClient.Client);
|
||||
|
@ -235,47 +235,47 @@ util.inherits(SSHClient, baseClient.Client);
|
|||
SSHClient.ValidAuthMethods = [ 'password', 'keyboard-interactive' ];
|
||||
|
||||
exports.getModule = class SSHServerModule extends LoginServerModule {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
createServer() {
|
||||
const config = Config();
|
||||
const serverConf = {
|
||||
hostKeys : [
|
||||
{
|
||||
key : fs.readFileSync(config.loginServers.ssh.privateKeyPem),
|
||||
passphrase : config.loginServers.ssh.privateKeyPass,
|
||||
}
|
||||
],
|
||||
ident : 'enigma-bbs-' + enigVersion + '-srv',
|
||||
createServer() {
|
||||
const config = Config();
|
||||
const serverConf = {
|
||||
hostKeys : [
|
||||
{
|
||||
key : fs.readFileSync(config.loginServers.ssh.privateKeyPem),
|
||||
passphrase : config.loginServers.ssh.privateKeyPass,
|
||||
}
|
||||
],
|
||||
ident : 'enigma-bbs-' + enigVersion + '-srv',
|
||||
|
||||
// Note that sending 'banner' breaks at least EtherTerm!
|
||||
debug : (sshDebugLine) => {
|
||||
if(true === config.loginServers.ssh.traceConnections) {
|
||||
Log.trace(`SSH: ${sshDebugLine}`);
|
||||
}
|
||||
},
|
||||
algorithms: { compress: ['none'] },
|
||||
};
|
||||
// Note that sending 'banner' breaks at least EtherTerm!
|
||||
debug : (sshDebugLine) => {
|
||||
if(true === config.loginServers.ssh.traceConnections) {
|
||||
Log.trace(`SSH: ${sshDebugLine}`);
|
||||
}
|
||||
},
|
||||
algorithms: { compress: ['none'] },
|
||||
};
|
||||
|
||||
this.server = ssh2.Server(serverConf);
|
||||
this.server.on('connection', (conn, info) => {
|
||||
Log.info(info, 'New SSH connection');
|
||||
this.handleNewClient(new SSHClient(conn), conn._sock, ModuleInfo);
|
||||
});
|
||||
}
|
||||
this.server = ssh2.Server(serverConf);
|
||||
this.server.on('connection', (conn, info) => {
|
||||
Log.info(info, 'New SSH connection');
|
||||
this.handleNewClient(new SSHClient(conn), conn._sock, ModuleInfo);
|
||||
});
|
||||
}
|
||||
|
||||
listen() {
|
||||
const config = Config();
|
||||
const port = parseInt(config.loginServers.ssh.port);
|
||||
if(isNaN(port)) {
|
||||
Log.error( { server : ModuleInfo.name, port : config.loginServers.ssh.port }, 'Cannot load server (invalid port)' );
|
||||
return false;
|
||||
}
|
||||
listen() {
|
||||
const config = Config();
|
||||
const port = parseInt(config.loginServers.ssh.port);
|
||||
if(isNaN(port)) {
|
||||
Log.error( { server : ModuleInfo.name, port : config.loginServers.ssh.port }, 'Cannot load server (invalid port)' );
|
||||
return false;
|
||||
}
|
||||
|
||||
this.server.listen(port);
|
||||
Log.info( { server : ModuleInfo.name, port : port }, 'Listening for connections' );
|
||||
return true;
|
||||
}
|
||||
this.server.listen(port);
|
||||
Log.info( { server : ModuleInfo.name, port : port }, 'Listening for connections' );
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue