2017-03-06 05:45:50 +08:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
state: 'Not Authenticated',
|
|
|
|
|
2017-06-03 14:51:58 +08:00
|
|
|
schema: [
|
|
|
|
{
|
|
|
|
name: 'token',
|
|
|
|
type: 'string',
|
|
|
|
optional: true
|
|
|
|
}
|
|
|
|
],
|
2017-03-06 05:45:50 +08:00
|
|
|
|
|
|
|
handler(command, callback, next) {
|
2017-06-03 14:51:58 +08:00
|
|
|
let token = ((command.attributes && command.attributes[0] && command.attributes[0].value) || '').toString().trim();
|
2017-03-06 05:45:50 +08:00
|
|
|
|
2017-10-07 20:12:06 +08:00
|
|
|
let requireClientToken = (command.command || '').toString().toUpperCase() === 'AUTHENTICATE PLAIN-CLIENTTOKEN' ? true : false;
|
|
|
|
|
2017-09-11 03:53:12 +08:00
|
|
|
if (!this.secure && !this._server.options.disableSTARTTLS && !this._server.options.ignoreSTARTTLS) {
|
2017-03-06 05:45:50 +08:00
|
|
|
// Only allow authentication using TLS
|
|
|
|
return callback(null, {
|
|
|
|
response: 'BAD',
|
|
|
|
message: 'Run STARTTLS first'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if authentication method is set
|
|
|
|
if (typeof this._server.onAuth !== 'function') {
|
|
|
|
return callback(null, {
|
|
|
|
response: 'NO',
|
|
|
|
message: 'Authentication not implemented'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!token) {
|
|
|
|
this._nextHandler = (token, next) => {
|
|
|
|
this._nextHandler = false;
|
|
|
|
next(); // keep the parser flowing
|
2017-10-07 20:12:06 +08:00
|
|
|
authenticate(this, token, requireClientToken, callback);
|
2017-03-06 05:45:50 +08:00
|
|
|
};
|
|
|
|
this.send('+');
|
|
|
|
return next(); // resume input parser. Normally this is done by callback() but we need the next input sooner
|
|
|
|
}
|
|
|
|
|
2017-10-07 20:12:06 +08:00
|
|
|
authenticate(this, token, requireClientToken, callback);
|
2017-03-06 05:45:50 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-10-07 20:12:06 +08:00
|
|
|
function authenticate(connection, token, requireClientToken, callback) {
|
2017-03-06 05:45:50 +08:00
|
|
|
let data = new Buffer(token, 'base64').toString().split('\x00');
|
|
|
|
|
2017-10-07 20:12:06 +08:00
|
|
|
if ((!requireClientToken && data.length !== 3) || (requireClientToken && data.length !== 4)) {
|
2017-03-06 05:45:50 +08:00
|
|
|
return callback(null, {
|
|
|
|
response: 'BAD',
|
|
|
|
message: 'Invalid SASL argument'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let username = (data[1] || '').toString().trim();
|
|
|
|
let password = (data[2] || '').toString().trim();
|
2017-10-07 20:12:06 +08:00
|
|
|
let clientToken = (data[3] || '').toString().trim() || false;
|
2017-03-06 05:45:50 +08:00
|
|
|
|
|
|
|
// Do auth
|
2017-06-03 14:51:58 +08:00
|
|
|
connection._server.onAuth(
|
|
|
|
{
|
|
|
|
method: 'PLAIN',
|
|
|
|
username,
|
2017-10-07 19:46:42 +08:00
|
|
|
password,
|
|
|
|
clientToken
|
2017-06-03 14:51:58 +08:00
|
|
|
},
|
|
|
|
connection.session,
|
|
|
|
(err, response) => {
|
|
|
|
if (err) {
|
|
|
|
connection._server.logger.info(
|
|
|
|
{
|
|
|
|
err,
|
|
|
|
tnx: 'auth',
|
|
|
|
username,
|
|
|
|
method: 'PLAIN',
|
|
|
|
action: 'fail',
|
|
|
|
cid: connection.id
|
|
|
|
},
|
|
|
|
'[%s] Authentication error for %s using %s\n%s',
|
|
|
|
connection.id,
|
|
|
|
username,
|
|
|
|
'PLAIN',
|
|
|
|
err.message
|
|
|
|
);
|
|
|
|
return callback(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!response || !response.user) {
|
|
|
|
connection._server.logger.info(
|
|
|
|
{
|
|
|
|
tnx: 'auth',
|
|
|
|
username,
|
|
|
|
method: 'PLAIN',
|
|
|
|
action: 'fail',
|
|
|
|
cid: connection.id
|
|
|
|
},
|
|
|
|
'[%s] Authentication failed for %s using %s',
|
|
|
|
connection.id,
|
|
|
|
username,
|
|
|
|
'PLAIN'
|
|
|
|
);
|
|
|
|
return callback(null, {
|
|
|
|
response: 'NO',
|
|
|
|
code: 'AUTHENTICATIONFAILED',
|
|
|
|
message: 'Invalid credentials'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
connection._server.logger.info(
|
|
|
|
{
|
|
|
|
tnx: 'auth',
|
|
|
|
username,
|
|
|
|
method: 'PLAIN',
|
|
|
|
action: 'success',
|
2017-10-07 20:12:06 +08:00
|
|
|
cid: connection.id,
|
|
|
|
clientToken
|
2017-06-03 14:51:58 +08:00
|
|
|
},
|
2017-10-07 20:12:06 +08:00
|
|
|
'[%s] %s authenticated using %s%s',
|
2017-06-03 14:51:58 +08:00
|
|
|
connection.id,
|
2017-05-16 18:57:04 +08:00
|
|
|
username,
|
2017-10-07 20:12:06 +08:00
|
|
|
'PLAIN' + (requireClientToken ? '-CLIENTTOKEN' : ''),
|
|
|
|
clientToken ? ' with token "' + clientToken + '"' : ''
|
2017-06-03 14:51:58 +08:00
|
|
|
);
|
2017-10-02 21:30:27 +08:00
|
|
|
|
|
|
|
connection.setUser(response.user);
|
2017-06-03 14:51:58 +08:00
|
|
|
connection.state = 'Authenticated';
|
|
|
|
|
|
|
|
callback(null, {
|
|
|
|
response: 'OK',
|
|
|
|
message: new Buffer(username + ' authenticated').toString('binary')
|
2017-03-06 05:45:50 +08:00
|
|
|
});
|
|
|
|
}
|
2017-06-03 14:51:58 +08:00
|
|
|
);
|
2017-03-06 05:45:50 +08:00
|
|
|
}
|