Skip to content

Commit 02f00b7

Browse files
committed
Support authentication switch request. Fixes #1396
Rename UseOldPasswordPacket to AuthenticationMethodSwitchRequestPacket and implement its two other fields: plugin name & data. Support mysql_native_password and mysql_old_password as potential authentication methods (but still require 'insecureAuth:true' for the latter).
1 parent 5c60778 commit 02f00b7

7 files changed

+76
-27
lines changed

lib/protocol/Parser.js

+5
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ Parser.prototype.parseLengthCodedBuffer = function() {
217217
return this.parseBuffer(length);
218218
};
219219

220+
Parser.prototype.parsePacketTerminatedBuffer = function() {
221+
var length = this._packetEnd - this._offset;
222+
return this.parseBuffer(length);
223+
};
224+
220225
Parser.prototype.parseLengthCodedNumber = function parseLengthCodedNumber() {
221226
if (this._offset >= this._buffer.length) {
222227
var err = new Error('Parser: read past end');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module.exports = AuthenticationMethodSwitchRequestPacket;
2+
function AuthenticationMethodSwitchRequestPacket(options) {
3+
options = options || {};
4+
5+
this.command = 0xfe;
6+
this.authMethodName = options.authMethodName || 'mysql_native_password';
7+
this.authPluginData = options.authPluginData;
8+
}
9+
10+
AuthenticationMethodSwitchRequestPacket.prototype.parse = function(parser) {
11+
this.command = parser.parseUnsignedNumber(1);
12+
this.authMethodName = parser.parseNullTerminatedString();
13+
this.authPluginData = parser.parsePacketTerminatedBuffer();
14+
};
15+
16+
AuthenticationMethodSwitchRequestPacket.prototype.write = function(writer) {
17+
writer.writeUnsignedNumber(1, this.command);
18+
writer.writeNullTerminatedString(this.authMethodName);
19+
writer.writeBuffer(this.authPluginData);
20+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = AuthenticationSwitchResponsePacket;
2+
function AuthenticationSwitchResponsePacket(options) {
3+
options = options || {};
4+
5+
this.scrambleBuff = options.scrambleBuff;
6+
}
7+
8+
AuthenticationSwitchResponsePacket.prototype.parse = function(parser) {
9+
this.scrambleBuff = parser.parsePacketTerminatedBuffer();
10+
};
11+
12+
AuthenticationSwitchResponsePacket.prototype.write = function(writer) {
13+
writer.writeBuffer(this.scrambleBuff);
14+
};

lib/protocol/packets/UseOldPasswordPacket.js

-14
This file was deleted.

lib/protocol/packets/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
exports.AuthenticationMethodSwitchRequestPacket = require('./AuthenticationMethodSwitchRequestPacket');
2+
exports.AuthenticationSwitchResponsePacket = require('./AuthenticationSwitchResponsePacket');
13
exports.ClientAuthenticationPacket = require('./ClientAuthenticationPacket');
24
exports.ComChangeUserPacket = require('./ComChangeUserPacket');
35
exports.ComPingPacket = require('./ComPingPacket');
@@ -17,4 +19,3 @@ exports.ResultSetHeaderPacket = require('./ResultSetHeaderPacket');
1719
exports.RowDataPacket = require('./RowDataPacket');
1820
exports.SSLRequestPacket = require('./SSLRequestPacket');
1921
exports.StatisticsPacket = require('./StatisticsPacket');
20-
exports.UseOldPasswordPacket = require('./UseOldPasswordPacket');

lib/protocol/sequences/Handshake.js

+34-11
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Handshake.prototype.determinePacket = function(firstByte) {
2525
}
2626

2727
if (firstByte === 0xfe) {
28-
return Packets.UseOldPasswordPacket;
28+
return Packets.AuthenticationMethodSwitchRequestPacket;
2929
}
3030

3131
return undefined;
@@ -80,23 +80,46 @@ Handshake.prototype._sendCredentials = function() {
8080
}));
8181
};
8282

83-
Handshake.prototype['UseOldPasswordPacket'] = function() {
84-
if (!this._config.insecureAuth) {
83+
Handshake.prototype['AuthenticationMethodSwitchRequestPacket'] = function(packet) {
84+
if (packet.authMethodName === 'mysql_native_password') {
85+
// "auth plugin data" is documented as "string[EOF]", but MySQL Server will send a
86+
// null-terminated byte array for mysql_native_password; we only need to hash with
87+
// the first 20 bytes
88+
var challenge = packet.authPluginData;
89+
if (challenge.length === 21) {
90+
challenge = challenge.slice(0, 20);
91+
}
92+
93+
this.emit('packet', new Packets.AuthenticationSwitchResponsePacket({
94+
scrambleBuff: Auth.token(this._config.password, challenge)
95+
}));
96+
} else if (packet.authMethodName === 'mysql_old_password') {
97+
if (!this._config.insecureAuth) {
98+
var err = new Error(
99+
'MySQL server is requesting the old and insecure pre-4.1 auth mechanism.' +
100+
'Upgrade the user password or use the {insecureAuth: true} option.'
101+
);
102+
103+
err.code = 'HANDSHAKE_INSECURE_AUTH';
104+
err.fatal = true;
105+
106+
this.end(err);
107+
return;
108+
}
109+
110+
this.emit('packet', new Packets.OldPasswordPacket({
111+
scrambleBuff: Auth.scramble323(this._handshakeInitializationPacket.scrambleBuff(), this._config.password)
112+
}));
113+
} else {
85114
var err = new Error(
86-
'MySQL server is requesting the old and insecure pre-4.1 auth mechanism.' +
87-
'Upgrade the user password or use the {insecureAuth: true} option.'
115+
'MySQL is requesting the ' + packet.authMethodName + ' authentication method, which is not supported.'
88116
);
89117

90-
err.code = 'HANDSHAKE_INSECURE_AUTH';
118+
err.code = 'UNSUPPORTED_AUTH_METHOD';
91119
err.fatal = true;
92120

93121
this.end(err);
94-
return;
95122
}
96-
97-
this.emit('packet', new Packets.OldPasswordPacket({
98-
scrambleBuff: Auth.scramble323(this._handshakeInitializationPacket.scrambleBuff(), this._config.password)
99-
}));
100123
};
101124

102125
Handshake.prototype['ErrorPacket'] = function(packet) {

test/FakeServer.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ FakeConnection.prototype._parsePacket = function(header) {
268268
case Packets.ClientAuthenticationPacket:
269269
this._clientAuthenticationPacket = packet;
270270
if (this._handshakeOptions.oldPassword) {
271-
this._sendPacket(new Packets.UseOldPasswordPacket());
271+
this._sendPacket(new Packets.AuthenticationMethodSwitchRequestPacket({ authMethodName: 'mysql_old_password' }));
272272
} else if (this._handshakeOptions.password === 'passwd') {
273273
var expected = Buffer.from('3DA0ADA7C9E1BB3A110575DF53306F9D2DE7FD09', 'hex');
274274
this._sendAuthResponse(packet, expected);

0 commit comments

Comments
 (0)