Skip to content

Commit

Permalink
Support auth switch in change user flow
Browse files Browse the repository at this point in the history
closes #1776
  • Loading branch information
elemount authored and dougwilson committed Apr 17, 2019
1 parent 776d843 commit c144dda
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 9 deletions.
1 change: 1 addition & 0 deletions Changes.md
Expand Up @@ -19,6 +19,7 @@ you spot any mistakes.
* Add Amazon RDS GovCloud SSL certificates #1876
* Add new error codes up to MySQL 5.7.21
* Include connection ID in debug output
* Support auth switch in change user flow #1776
* Support Node.js 9.x
* Support Node.js 10.x #2003 #2024 #2026 #2034
* Update Amazon RDS SSL certificates
Expand Down
12 changes: 12 additions & 0 deletions lib/protocol/Auth.js
Expand Up @@ -2,6 +2,18 @@ var Buffer = require('safe-buffer').Buffer;
var Crypto = require('crypto');
var Auth = exports;

function auth(name, data, options) {
options = options || {};

switch (name) {
case 'mysql_native_password':
return Auth.token(options.password, data.slice(0, 20));
default:
return undefined;
}
}
Auth.auth = auth;

function sha1(msg) {
var hash = Crypto.createHash('sha1');
hash.update(msg, 'binary');
Expand Down
26 changes: 26 additions & 0 deletions lib/protocol/sequences/ChangeUser.js
Expand Up @@ -15,6 +15,14 @@ function ChangeUser(options, callback) {
this._currentConfig = options.currentConfig;
}

ChangeUser.prototype.determinePacket = function determinePacket(firstByte) {
switch (firstByte) {
case 0xfe: return Packets.AuthSwitchRequestPacket;
case 0xff: return Packets.ErrorPacket;
default: return undefined;
}
};

ChangeUser.prototype.start = function(handshakeInitializationPacket) {
var scrambleBuff = handshakeInitializationPacket.scrambleBuff();
scrambleBuff = Auth.token(this._password, scrambleBuff);
Expand All @@ -34,6 +42,24 @@ ChangeUser.prototype.start = function(handshakeInitializationPacket) {
this.emit('packet', packet);
};

ChangeUser.prototype['AuthSwitchRequestPacket'] = function (packet) {
var name = packet.authMethodName;
var data = Auth.auth(name, packet.authMethodData, {
password: this._password
});

if (data !== undefined) {
this.emit('packet', new Packets.AuthSwitchResponsePacket({
data: data
}));
} else {
var err = new Error('MySQL is requesting the ' + name + ' authentication method, which is not supported.');
err.code = 'UNSUPPORTED_AUTH_METHOD';
err.fatal = true;
this.end(err);
}
};

ChangeUser.prototype['ErrorPacket'] = function(packet) {
var err = this._packetToError(packet);
err.fatal = true;
Expand Down
17 changes: 8 additions & 9 deletions lib/protocol/sequences/Handshake.js
Expand Up @@ -34,20 +34,19 @@ Handshake.prototype.determinePacket = function determinePacket(firstByte, parser
};

Handshake.prototype['AuthSwitchRequestPacket'] = function (packet) {
if (packet.authMethodName === 'mysql_native_password') {
var challenge = packet.authMethodData.slice(0, 20);
var name = packet.authMethodName;
var data = Auth.auth(name, packet.authMethodData, {
password: this._config.password
});

if (data !== undefined) {
this.emit('packet', new Packets.AuthSwitchResponsePacket({
data: Auth.token(this._config.password, challenge)
data: data
}));
} else {
var err = new Error(
'MySQL is requesting the ' + packet.authMethodName + ' authentication method, which is not supported.'
);

err.code = 'UNSUPPORTED_AUTH_METHOD';
var err = new Error('MySQL is requesting the ' + name + ' authentication method, which is not supported.');
err.code = 'UNSUPPORTED_AUTH_METHOD';
err.fatal = true;

this.end(err);
}
};
Expand Down
40 changes: 40 additions & 0 deletions test/unit/connection/test-change-user-auth-switch-unknown.js
@@ -0,0 +1,40 @@
var assert = require('assert');
var Buffer = require('safe-buffer').Buffer;
var common = require('../../common');
var connection = common.createConnection({
port : common.fakeServerPort,
user : 'user_1',
password : 'pass_1'
});

var server = common.createFakeServer();

server.listen(common.fakeServerPort, function(err) {
assert.ifError(err);

connection.query('SELECT CURRENT_USER()', function (err, result) {
assert.ifError(err);
assert.strictEqual(result[0]['CURRENT_USER()'], 'user_1@localhost');

connection.changeUser({user: 'user_2', password: 'pass_2'}, function (err) {
assert.ok(err);
assert.equal(err.code, 'UNSUPPORTED_AUTH_METHOD');
assert.equal(err.fatal, true);
assert.ok(/foo_plugin_password/.test(err.message));

connection.destroy();
server.destroy();
});
});
});

server.on('connection', function (incomingConnection) {
incomingConnection.on('changeUser', function () {
this.authSwitchRequest({
authMethodName : 'foo_plugin_password',
authMethodData : Buffer.alloc(0)
});
});

incomingConnection.handshake();
});
45 changes: 45 additions & 0 deletions test/unit/connection/test-change-user-auth-switch.js
@@ -0,0 +1,45 @@
var assert = require('assert');
var Crypto = require('crypto');
var common = require('../../common');
var connection = common.createConnection({
port : common.fakeServerPort,
user : 'user_1',
password : 'pass_1'
});

var random = Crypto.pseudoRandomBytes || Crypto.randomBytes; // Depends on node.js version
var server = common.createFakeServer();

server.listen(common.fakeServerPort, function(err) {
assert.ifError(err);

connection.query('SELECT CURRENT_USER()', function (err, result) {
assert.ifError(err);
assert.strictEqual(result[0]['CURRENT_USER()'], 'user_1@localhost');

connection.changeUser({user: 'user_2', password: 'pass_2'}, function (err) {
assert.ifError(err);
connection.destroy();
server.destroy();
});
});
});

server.on('connection', function (incomingConnection) {
random(20, function (err, scramble) {
assert.ifError(err);

incomingConnection.on('authSwitchResponse', function (packet) {
this._sendAuthResponse(packet.data, common.Auth.token('pass_2', scramble));
});

incomingConnection.on('changeUser', function () {
this.authSwitchRequest({
authMethodName : 'mysql_native_password',
authMethodData : scramble
});
});

incomingConnection.handshake();
});
});

0 comments on commit c144dda

Please sign in to comment.