123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- 'use strict';
- const Command = require('../command');
- const InitialHandshake = require('./initial-handshake');
- const ClientHandshakeResponse = require('./client-handshake-response');
- const SslRequest = require('./ssl-request');
- const ClientCapabilities = require('./client-capabilities');
- const Errors = require('../../misc/errors');
- const Capabilities = require('../../const/capabilities');
- const process = require('process');
- /**
- * Handle handshake.
- * see https://mariadb.com/kb/en/library/1-connecting-connecting/
- */
- class Handshake extends Command {
- constructor(resolve, reject, _createSecureContext, _addCommand, getSocket) {
- super(resolve, reject);
- this._createSecureContext = _createSecureContext;
- this._addCommand = _addCommand;
- this.getSocket = getSocket;
- this.onPacketReceive = this.parseHandshakeInit;
- this.plugin = this;
- }
- ensureOptionCompatibility(opts, info) {
- if (
- opts.multipleStatements &&
- (info.serverCapabilities & Capabilities.MULTI_STATEMENTS) === 0
- ) {
- return this.throwNewError(
- "Option `multipleStatements` enable, but server doesn'permits multi-statment",
- true,
- info,
- '08S01',
- Errors.ER_CLIENT_OPTION_INCOMPATIBILITY
- );
- }
- if (opts.permitLocalInfile && (info.serverCapabilities & Capabilities.LOCAL_FILES) === 0) {
- return this.throwNewError(
- "Option `permitLocalInfile` enable, but server doesn'permits using local file",
- true,
- info,
- '08S01',
- Errors.ER_CLIENT_OPTION_INCOMPATIBILITY
- );
- }
- }
- parseHandshakeInit(packet, out, opts, info) {
- if (packet.peek() === 0xff) {
- //in case that some host is not permit to connect server
- const authErr = packet.readError(info);
- authErr.fatal = true;
- return this.throwError(authErr, info);
- }
- let handshake = new InitialHandshake(packet, info);
- this.ensureOptionCompatibility(opts, info);
- ClientCapabilities.init(opts, info);
- if (opts.ssl) {
- if (info.serverCapabilities & Capabilities.SSL) {
- info.clientCapabilities |= Capabilities.SSL;
- SslRequest.send(this, out, info, opts);
- this._createSecureContext(
- function () {
- ClientHandshakeResponse.send(this, out, opts, handshake.pluginName, info);
- }.bind(this)
- );
- } else {
- return this.throwNewError(
- 'Trying to connect with ssl, but ssl not enabled in the server',
- true,
- info,
- '08S01',
- Errors.ER_SERVER_SSL_DISABLED
- );
- }
- } else {
- ClientHandshakeResponse.send(this, out, opts, handshake.pluginName, info);
- }
- this.onPacketReceive = this.handshakeResult;
- }
- /**
- * Fast-path handshake results :
- * - if plugin was the one expected by server, server will send OK_Packet / ERR_Packet.
- * - if not, server send an AuthSwitchRequest packet, indicating the specific PLUGIN to use with this user.
- * dispatching to plugin handler then.
- *
- * @param packet current packet
- * @param out output buffer
- * @param opts options
- * @param info connection info
- * @returns {*} return null if authentication succeed, depending on plugin conversation if not finished
- */
- handshakeResult(packet, out, opts, info) {
- const marker = packet.peek();
- switch (marker) {
- //*********************************************************************************************************
- //* AuthSwitchRequest packet
- //*********************************************************************************************************
- case 0xfe:
- this.plugin.onPacketReceive = null;
- this.plugin.emit('send_end');
- this.plugin.emit('end');
- this.dispatchAuthSwitchRequest(packet, out, opts, info);
- return;
- //*********************************************************************************************************
- //* OK_Packet - authentication succeeded
- //*********************************************************************************************************
- case 0x00:
- packet.skip(1); //skip header
- packet.skipLengthCodedNumber(); //skip affected rows
- packet.skipLengthCodedNumber(); //skip last insert id
- info.status = packet.readUInt16();
- this.plugin.emit('send_end');
- return this.plugin.successEnd();
- //*********************************************************************************************************
- //* ERR_Packet
- //*********************************************************************************************************
- case 0xff:
- const authErr = packet.readError(info, this.displaySql());
- authErr.fatal = true;
- return this.plugin.throwError(authErr, info);
- //*********************************************************************************************************
- //* unexpected
- //*********************************************************************************************************
- default:
- this.throwNewError(
- 'Unexpected type of packet during handshake phase : ' + marker,
- true,
- info,
- '42000',
- Errors.ER_AUTHENTICATION_BAD_PACKET
- );
- }
- }
- /**
- * Handle authentication switch request : dispatch to plugin handler.
- *
- * @param packet packet
- * @param out output writer
- * @param opts options
- * @param info connection information
- */
- dispatchAuthSwitchRequest(packet, out, opts, info) {
- let pluginName, pluginData;
- if (info.clientCapabilities & Capabilities.PLUGIN_AUTH) {
- packet.skip(1); //header
- if (packet.remaining()) {
- //AuthSwitchRequest packet.
- pluginName = packet.readStringNullEnded();
- pluginData = packet.readBufferRemaining();
- } else {
- //OldAuthSwitchRequest
- pluginName = 'mysql_old_password';
- pluginData = info.seed.slice(0, 8);
- }
- } else {
- pluginName = packet.readStringNullEnded('cesu8');
- pluginData = packet.readBufferRemaining();
- }
- try {
- this.plugin = Handshake.pluginHandler(
- pluginName,
- this.plugin.sequenceNo,
- this.plugin.compressSequenceNo,
- pluginData,
- info,
- opts,
- out,
- this.resolve,
- this.reject,
- this.handshakeResult.bind(this)
- );
- } catch (err) {
- this.reject(err);
- return;
- }
- if (!this.plugin) {
- this.reject(
- Errors.createError(
- "Client does not support authentication protocol '" +
- pluginName +
- "' requested by server. ",
- true,
- info,
- '08004',
- Errors.ER_AUTHENTICATION_PLUGIN_NOT_SUPPORTED
- )
- );
- } else {
- this._addCommand(this.plugin, false);
- }
- }
- static pluginHandler(
- pluginName,
- packSeq,
- compressPackSeq,
- pluginData,
- info,
- opts,
- out,
- authResolve,
- authReject,
- multiAuthResolver
- ) {
- let pluginAuth;
- switch (pluginName) {
- case 'mysql_native_password':
- pluginAuth = require('./auth/native-password-auth.js');
- break;
- case 'mysql_clear_password':
- pluginAuth = require('./auth/clear-password-auth.js');
- break;
- case 'client_ed25519':
- pluginAuth = require('./auth/ed25519-password-auth.js');
- break;
- case 'dialog':
- pluginAuth = require('./auth/pam-password-auth.js');
- break;
- case 'sha256_password':
- if (!Handshake.ensureNodeVersion(11, 6, 0)) {
- throw Errors.createError(
- 'sha256_password authentication plugin require node 11.6+',
- true,
- info,
- '08004',
- Errors.ER_MINIMUM_NODE_VERSION_REQUIRED
- );
- }
- pluginAuth = require('./auth/sha256-password-auth.js');
- break;
- case 'caching_sha2_password':
- if (!Handshake.ensureNodeVersion(11, 6, 0)) {
- throw Errors.createError(
- 'caching_sha2_password authentication plugin require node 11.6+',
- true,
- info,
- '08004',
- Errors.ER_MINIMUM_NODE_VERSION_REQUIRED
- );
- }
- pluginAuth = require('./auth/caching-sha2-password-auth.js');
- break;
- //TODO "auth_gssapi_client"
- default:
- return null;
- }
- return new pluginAuth(
- packSeq,
- compressPackSeq,
- pluginData,
- authResolve,
- authReject,
- multiAuthResolver
- );
- }
- static ensureNodeVersion(major, minor, patch) {
- const ver = process.versions.node.split('.');
- return (
- ver[0] > major ||
- (ver[0] === major && ver[1] > minor) ||
- (ver[0] === major && ver[1] === minor && ver[2] >= patch)
- );
- }
- }
- module.exports = Handshake;
|