-
-
Notifications
You must be signed in to change notification settings - Fork 668
Expand file tree
/
Copy pathauth_switch.js
More file actions
153 lines (136 loc) · 5.02 KB
/
auth_switch.js
File metadata and controls
153 lines (136 loc) · 5.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// This file was modified by Oracle on July 5, 2021.
// Errors generated by asynchronous authentication plugins are now being
// handled and subsequently emitted at the command level.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
'use strict';
const Packets = require('../packets/index.js');
const sha256_password = require('../auth_plugins/sha256_password');
const caching_sha2_password = require('../auth_plugins/caching_sha2_password.js');
const mysql_native_password = require('../auth_plugins/mysql_native_password.js');
const mysql_clear_password = require('../auth_plugins/mysql_clear_password.js');
// Use Object.create(null) to avoid prototype pollution
// This prevents server-controlled pluginName values like "toString" or "__proto__"
// from resolving to prototype properties
const standardAuthPlugins = Object.assign(Object.create(null), {
sha256_password: sha256_password({}),
caching_sha2_password: caching_sha2_password({}),
mysql_native_password: mysql_native_password({}),
mysql_clear_password: mysql_clear_password({}),
});
// Helper function to get auth plugin (custom or standard)
function getAuthPlugin(pluginName, connection) {
const customPlugins = connection.config.authPlugins;
// Check custom plugins with hasOwnProperty for safety
if (
customPlugins &&
Object.prototype.hasOwnProperty.call(customPlugins, pluginName)
) {
return customPlugins[pluginName];
}
// Safe to access standardAuthPlugins directly since it has no prototype
return standardAuthPlugins[pluginName];
}
function warnLegacyAuthSwitch() {
console.warn(
'WARNING! authSwitchHandler api is deprecated, please use new authPlugins api'
);
}
function authSwitchPluginError(error, command) {
// Authentication errors are fatal
error.code = 'AUTH_SWITCH_PLUGIN_ERROR';
error.fatal = true;
command.emit('error', error);
}
function authSwitchRequest(packet, connection, command) {
const { pluginName, pluginData } =
Packets.AuthSwitchRequest.fromPacket(packet);
// legacy plugin api don't allow to override mysql_native_password
// if pluginName is mysql_native_password it's using standard auth4.1 auth
if (
connection.config.authSwitchHandler &&
pluginName !== 'mysql_native_password'
) {
const legacySwitchHandler = connection.config.authSwitchHandler;
warnLegacyAuthSwitch();
legacySwitchHandler({ pluginName, pluginData }, (err, data) => {
if (err) {
return authSwitchPluginError(err, command);
}
connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());
});
return;
}
if (pluginName === 'mysql_clear_password') {
const hasCustomPlugin =
connection.config.authPlugins &&
Object.prototype.hasOwnProperty.call(
connection.config.authPlugins,
'mysql_clear_password'
);
if (!hasCustomPlugin && !connection.config.enableCleartextPlugin) {
const err = new Error(
'Server requested authentication using mysql_clear_password, ' +
'which sends the password in plaintext over the network and is ' +
'disabled by default. To enable it, set the `enableCleartextPlugin` ' +
'option to `true` in your connection configuration, or provide a ' +
'custom `mysql_clear_password` auth plugin via the `authPlugins` ' +
'option. Only use this over a secure connection (TLS/SSL).'
);
err.code = 'MYSQL_CLEAR_PASSWORD_NOT_ENABLED';
err.fatal = true;
throw err;
}
}
const authPlugin = getAuthPlugin(pluginName, connection);
if (!authPlugin) {
const err = new Error(
`Server requests authentication using unknown plugin ${pluginName}.`
);
connection.emit('error', err);
return;
}
connection._authPlugin = authPlugin({ connection, command });
Promise.resolve(connection._authPlugin(pluginData))
.then((data) => {
if (data) {
connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());
}
})
.catch((err) => {
authSwitchPluginError(err, command);
});
}
function authSwitchRequestMoreData(packet, connection, command) {
const { data } = Packets.AuthSwitchRequestMoreData.fromPacket(packet);
if (connection.config.authSwitchHandler) {
const legacySwitchHandler = connection.config.authSwitchHandler;
warnLegacyAuthSwitch();
legacySwitchHandler({ pluginData: data }, (err, data) => {
if (err) {
return authSwitchPluginError(err, command);
}
connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());
});
return;
}
if (!connection._authPlugin) {
throw new Error(
'AuthPluginMoreData received but no auth plugin instance found'
);
}
Promise.resolve(connection._authPlugin(data))
.then((data) => {
if (data) {
connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());
}
})
.catch((err) => {
authSwitchPluginError(err, command);
});
}
module.exports = {
authSwitchRequest,
authSwitchRequestMoreData,
getAuthPlugin,
standardAuthPlugins,
};