@tachybase/module-multi-app 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -34
- package/README.zh-CN.md +34 -34
- package/client.d.ts +1 -1
- package/client.js +1 -1
- package/dist/externalVersion.js +5 -5
- package/dist/locale/en-US.json +48 -48
- package/dist/locale/es-ES.json +9 -9
- package/dist/locale/ko_KR.json +11 -11
- package/dist/locale/pt-BR.json +9 -9
- package/dist/locale/zh-CN.json +58 -58
- package/dist/node_modules/mariadb/callback.js +43 -8
- package/dist/node_modules/mariadb/check-node.js +30 -0
- package/dist/node_modules/mariadb/lib/cluster-callback.js +84 -0
- package/dist/node_modules/mariadb/lib/cluster.js +446 -0
- package/dist/node_modules/mariadb/lib/cmd/batch-bulk.js +576 -177
- package/dist/node_modules/mariadb/lib/cmd/change-user.js +54 -44
- package/dist/node_modules/mariadb/lib/cmd/class/ok-packet.js +3 -2
- package/dist/node_modules/mariadb/lib/cmd/class/prepare-cache-wrapper.js +46 -0
- package/dist/node_modules/mariadb/lib/cmd/class/prepare-result-packet.js +141 -0
- package/dist/node_modules/mariadb/lib/cmd/class/prepare-wrapper.js +70 -0
- package/dist/node_modules/mariadb/lib/cmd/close-prepare.js +38 -0
- package/dist/node_modules/mariadb/lib/cmd/column-definition.js +145 -47
- package/dist/node_modules/mariadb/lib/cmd/command.js +41 -75
- package/dist/node_modules/mariadb/lib/cmd/decoder/binary-decoder.js +282 -0
- package/dist/node_modules/mariadb/lib/cmd/decoder/text-decoder.js +210 -0
- package/dist/node_modules/mariadb/lib/cmd/{common-binary-cmd.js → encoder/binary-encoder.js} +34 -77
- package/dist/node_modules/mariadb/lib/cmd/encoder/text-encoder.js +311 -0
- package/dist/node_modules/mariadb/lib/cmd/execute-stream.js +61 -0
- package/dist/node_modules/mariadb/lib/cmd/execute.js +338 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/caching-sha2-password-auth.js +25 -62
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/clear-password-auth.js +39 -6
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/ed25519-password-auth.js +48 -16
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/handshake.js +198 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/{initial-handshake.js → auth/initial-handshake.js} +10 -8
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/native-password-auth.js +22 -9
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/pam-password-auth.js +9 -4
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/parsec-auth.js +115 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/plugin-auth.js +12 -5
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/sha256-password-auth.js +44 -33
- package/dist/node_modules/mariadb/lib/cmd/handshake/authentication.js +335 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/client-capabilities.js +20 -19
- package/dist/node_modules/mariadb/lib/cmd/handshake/ssl-request.js +6 -3
- package/dist/node_modules/mariadb/lib/cmd/parser.js +861 -0
- package/dist/node_modules/mariadb/lib/cmd/ping.js +17 -18
- package/dist/node_modules/mariadb/lib/cmd/prepare.js +170 -0
- package/dist/node_modules/mariadb/lib/cmd/query.js +281 -144
- package/dist/node_modules/mariadb/lib/cmd/quit.js +9 -6
- package/dist/node_modules/mariadb/lib/cmd/reset.js +15 -19
- package/dist/node_modules/mariadb/lib/cmd/stream.js +21 -6
- package/dist/node_modules/mariadb/lib/config/cluster-options.js +23 -0
- package/dist/node_modules/mariadb/lib/config/connection-options.js +196 -132
- package/dist/node_modules/mariadb/lib/config/pool-options.js +27 -19
- package/dist/node_modules/mariadb/lib/connection-callback.js +492 -120
- package/dist/node_modules/mariadb/lib/connection-promise.js +372 -0
- package/dist/node_modules/mariadb/lib/connection.js +1739 -1016
- package/dist/node_modules/mariadb/lib/const/capabilities.js +36 -30
- package/dist/node_modules/mariadb/lib/const/collations.js +972 -36
- package/dist/node_modules/mariadb/lib/const/connection_status.js +3 -0
- package/dist/node_modules/mariadb/lib/const/error-code.js +35 -11
- package/dist/node_modules/mariadb/lib/const/field-detail.js +3 -0
- package/dist/node_modules/mariadb/lib/const/field-type.js +7 -4
- package/dist/node_modules/mariadb/lib/const/server-status.js +4 -1
- package/dist/node_modules/mariadb/lib/const/state-change.js +3 -0
- package/dist/node_modules/mariadb/lib/filtered-cluster-callback.js +136 -0
- package/dist/node_modules/mariadb/lib/filtered-cluster.js +118 -0
- package/dist/node_modules/mariadb/lib/io/compression-input-stream.js +14 -13
- package/dist/node_modules/mariadb/lib/io/compression-output-stream.js +21 -18
- package/dist/node_modules/mariadb/lib/io/packet-input-stream.js +75 -64
- package/dist/node_modules/mariadb/lib/io/packet-node-encoded.js +13 -9
- package/dist/node_modules/mariadb/lib/io/packet-node-iconv.js +12 -10
- package/dist/node_modules/mariadb/lib/io/packet-output-stream.js +402 -134
- package/dist/node_modules/mariadb/lib/io/packet.js +287 -202
- package/dist/node_modules/mariadb/lib/lru-prepare-cache.js +84 -0
- package/dist/node_modules/mariadb/lib/misc/connection-information.js +15 -32
- package/dist/node_modules/mariadb/lib/misc/errors.js +68 -25
- package/dist/node_modules/mariadb/lib/misc/parse.js +207 -711
- package/dist/node_modules/mariadb/lib/misc/utils.js +34 -62
- package/dist/node_modules/mariadb/lib/pool-callback.js +213 -174
- package/dist/node_modules/mariadb/lib/pool-promise.js +228 -94
- package/dist/node_modules/mariadb/lib/pool.js +951 -0
- package/dist/node_modules/mariadb/package.json +1 -1
- package/dist/node_modules/mariadb/promise.js +1 -34
- package/dist/node_modules/mariadb/types/callback.d.ts +207 -0
- package/dist/node_modules/mariadb/types/index.d.ts +94 -674
- package/dist/node_modules/mariadb/types/share.d.ts +804 -0
- package/dist/node_modules/qs/package.json +1 -1
- package/dist/server/actions/apps.js +2 -2
- package/dist/server/app-lifecycle.d.ts +1 -1
- package/dist/server/app-lifecycle.js +4 -4
- package/dist/server/models/application.d.ts +1 -1
- package/package.json +7 -7
- package/server.d.ts +2 -2
- package/server.js +1 -1
- package/dist/node_modules/mariadb/lib/cmd/batch-rewrite.js +0 -372
- package/dist/node_modules/mariadb/lib/cmd/common-text-cmd.js +0 -427
- package/dist/node_modules/mariadb/lib/cmd/handshake/client-handshake-response.js +0 -126
- package/dist/node_modules/mariadb/lib/cmd/handshake/handshake.js +0 -292
- package/dist/node_modules/mariadb/lib/cmd/resultset.js +0 -607
- package/dist/node_modules/mariadb/lib/config/pool-cluster-options.js +0 -19
- package/dist/node_modules/mariadb/lib/filtered-pool-cluster.js +0 -81
- package/dist/node_modules/mariadb/lib/io/bulk-packet.js +0 -590
- package/dist/node_modules/mariadb/lib/io/rewrite-packet.js +0 -481
- package/dist/node_modules/mariadb/lib/pool-base.js +0 -611
- package/dist/node_modules/mariadb/lib/pool-cluster-callback.js +0 -66
- package/dist/node_modules/mariadb/lib/pool-cluster.js +0 -407
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
2
|
+
// Copyright (c) 2015-2024 MariaDB Corporation Ab
|
|
3
|
+
|
|
1
4
|
const PluginAuth = require('./plugin-auth');
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Use PAM authentication
|
|
5
8
|
*/
|
|
6
9
|
class PamPasswordAuth extends PluginAuth {
|
|
7
|
-
constructor(packSeq, compressPackSeq, pluginData,
|
|
8
|
-
super(
|
|
10
|
+
constructor(packSeq, compressPackSeq, pluginData, cmdParam, reject, multiAuthResolver) {
|
|
11
|
+
super(cmdParam, multiAuthResolver, reject);
|
|
9
12
|
this.pluginData = pluginData;
|
|
10
13
|
this.sequenceNo = packSeq;
|
|
14
|
+
this.compressSequenceNo = compressPackSeq;
|
|
11
15
|
this.counter = 0;
|
|
16
|
+
this.multiAuthResolver = multiAuthResolver;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
start(out, opts, info) {
|
|
@@ -33,7 +38,7 @@ class PamPasswordAuth extends PluginAuth {
|
|
|
33
38
|
|
|
34
39
|
if (pwd) out.writeString(pwd);
|
|
35
40
|
out.writeInt8(0);
|
|
36
|
-
out.
|
|
41
|
+
out.flushPacket();
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
response(packet, out, opts, info) {
|
|
@@ -45,7 +50,7 @@ class PamPasswordAuth extends PluginAuth {
|
|
|
45
50
|
case 0x00:
|
|
46
51
|
case 0xff:
|
|
47
52
|
this.emit('send_end');
|
|
48
|
-
return this.
|
|
53
|
+
return this.multiAuthResolver(packet, out, opts, info);
|
|
49
54
|
|
|
50
55
|
default:
|
|
51
56
|
let promptData = packet.readBuffer();
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
2
|
+
// Copyright (c) 2015-2025 MariaDB Corporation Ab
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const PluginAuth = require('./plugin-auth');
|
|
7
|
+
const crypto = require('crypto');
|
|
8
|
+
const Errors = require('../../../misc/errors');
|
|
9
|
+
|
|
10
|
+
const pkcs8Ed25519header = Buffer.from([
|
|
11
|
+
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Standard authentication plugin
|
|
16
|
+
*/
|
|
17
|
+
class ParsecAuth extends PluginAuth {
|
|
18
|
+
#hash;
|
|
19
|
+
constructor(packSeq, compressPackSeq, pluginData, cmdParam, reject, multiAuthResolver) {
|
|
20
|
+
super(cmdParam, multiAuthResolver, reject);
|
|
21
|
+
this.multiAuthResolver = multiAuthResolver;
|
|
22
|
+
this.pluginData = pluginData;
|
|
23
|
+
this.sequenceNo = packSeq;
|
|
24
|
+
this.compressSequenceNo = compressPackSeq;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
start(out, opts, info) {
|
|
28
|
+
if (!info.extSalt) {
|
|
29
|
+
out.startPacket(this);
|
|
30
|
+
out.writeEmptyPacket(true); // indicate need salt
|
|
31
|
+
this.onPacketReceive = this.requestForSalt;
|
|
32
|
+
} else {
|
|
33
|
+
this.parseExtSalt(Buffer.from(info.extSalt, 'hex'), info);
|
|
34
|
+
this.sendScramble(out, opts, info);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
requestForSalt(packet, out, opts, info) {
|
|
39
|
+
this.parseExtSalt(packet.readBufferRemaining(), info);
|
|
40
|
+
this.sendScramble(out, opts, info);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
parseExtSalt(extSalt, info) {
|
|
44
|
+
if (extSalt.length < 2 || extSalt[0] !== 0x50 || extSalt[1] > 3) {
|
|
45
|
+
// expected 'P' for KDF algorithm (PBKDF2) and maximum iteration of 8192
|
|
46
|
+
return this.throwError(
|
|
47
|
+
Errors.createFatalError('Wrong parsec authentication format', Errors.ER_AUTHENTICATION_BAD_PACKET, info),
|
|
48
|
+
info
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
this.iterations = extSalt[1];
|
|
52
|
+
this.salt = extSalt.slice(2);
|
|
53
|
+
|
|
54
|
+
// disable for now until https://jira.mariadb.org/browse/MDEV-34846
|
|
55
|
+
// info.extSalt = extSalt.toString('hex');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
sendScramble(out, opts, info) {
|
|
59
|
+
const derivedKey = crypto.pbkdf2Sync(opts.password || '', this.salt, 1024 << this.iterations, 32, 'sha512');
|
|
60
|
+
const privateKey = toPkcs8der(derivedKey);
|
|
61
|
+
|
|
62
|
+
const rawPublicKey = this.getEd25519PublicKeyFromPrivateKey(derivedKey);
|
|
63
|
+
|
|
64
|
+
this.#hash = Buffer.concat([Buffer.from([0x50, this.iterations]), this.salt, rawPublicKey]);
|
|
65
|
+
|
|
66
|
+
const client_scramble = crypto.randomBytes(32);
|
|
67
|
+
const message = Buffer.concat([this.pluginData, client_scramble]);
|
|
68
|
+
const signature = crypto.sign(null, message, privateKey);
|
|
69
|
+
|
|
70
|
+
out.startPacket(this);
|
|
71
|
+
out.writeBuffer(client_scramble, 0, 32);
|
|
72
|
+
out.writeBuffer(signature, 0, 64);
|
|
73
|
+
out.flushPacket();
|
|
74
|
+
this.emit('send_end');
|
|
75
|
+
this.onPacketReceive = this.multiAuthResolver;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getEd25519PublicKeyFromPrivateKey(privateKeyBuffer) {
|
|
79
|
+
// Create a KeyObject from the raw private key
|
|
80
|
+
const privateKey = crypto.createPrivateKey({
|
|
81
|
+
key: Buffer.concat([pkcs8Ed25519header, privateKeyBuffer]),
|
|
82
|
+
format: 'der',
|
|
83
|
+
type: 'pkcs8',
|
|
84
|
+
name: 'ed25519'
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Get the corresponding public key
|
|
88
|
+
const publicKey = crypto.createPublicKey(privateKey);
|
|
89
|
+
|
|
90
|
+
// Export the public key in raw format
|
|
91
|
+
return publicKey
|
|
92
|
+
.export({
|
|
93
|
+
type: 'spki',
|
|
94
|
+
format: 'der'
|
|
95
|
+
})
|
|
96
|
+
.subarray(-32); // The last 32 bytes contain the raw key
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
permitHash() {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
hash(conf) {
|
|
104
|
+
return this.#hash;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const toPkcs8der = (rawB64) => {
|
|
109
|
+
// prefix for a private Ed25519
|
|
110
|
+
const prefixPrivateEd25519 = Buffer.from('302e020100300506032b657004220420', 'hex');
|
|
111
|
+
const der = Buffer.concat([prefixPrivateEd25519, rawB64]);
|
|
112
|
+
return crypto.createPrivateKey({ key: der, format: 'der', type: 'pkcs8' });
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
module.exports = ParsecAuth;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
2
|
+
// Copyright (c) 2015-2024 MariaDB Corporation Ab
|
|
3
|
+
|
|
1
4
|
'use strict';
|
|
2
5
|
|
|
3
6
|
const Command = require('../../command');
|
|
@@ -6,13 +9,17 @@ const Command = require('../../command');
|
|
|
6
9
|
* Base authentication plugin
|
|
7
10
|
*/
|
|
8
11
|
class PluginAuth extends Command {
|
|
9
|
-
constructor(
|
|
10
|
-
super(
|
|
11
|
-
this.
|
|
12
|
+
constructor(cmdParam, multiAuthResolver, reject) {
|
|
13
|
+
super(cmdParam, multiAuthResolver, reject);
|
|
14
|
+
this.onPacketReceive = multiAuthResolver;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
permitHash() {
|
|
18
|
+
return true;
|
|
12
19
|
}
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
hash(conf) {
|
|
22
|
+
return null;
|
|
16
23
|
}
|
|
17
24
|
}
|
|
18
25
|
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
2
|
+
// Copyright (c) 2015-2024 MariaDB Corporation Ab
|
|
3
|
+
|
|
1
4
|
const PluginAuth = require('./plugin-auth');
|
|
2
5
|
const fs = require('fs');
|
|
3
6
|
const crypto = require('crypto');
|
|
4
7
|
const Errors = require('../../../misc/errors');
|
|
8
|
+
const Crypto = require('crypto');
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
11
|
* Use Sha256 authentication
|
|
8
12
|
*/
|
|
9
13
|
class Sha256PasswordAuth extends PluginAuth {
|
|
10
|
-
constructor(packSeq, compressPackSeq, pluginData,
|
|
11
|
-
super(
|
|
14
|
+
constructor(packSeq, compressPackSeq, pluginData, cmdParam, reject, multiAuthResolver) {
|
|
15
|
+
super(cmdParam, multiAuthResolver, reject);
|
|
12
16
|
this.pluginData = pluginData;
|
|
13
17
|
this.sequenceNo = packSeq;
|
|
18
|
+
this.compressSequenceNo = compressPackSeq;
|
|
19
|
+
this.counter = 0;
|
|
14
20
|
this.counter = 0;
|
|
15
21
|
this.initialState = true;
|
|
22
|
+
this.multiAuthResolver = multiAuthResolver;
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
start(out, opts, info) {
|
|
@@ -33,7 +40,7 @@ class Sha256PasswordAuth extends PluginAuth {
|
|
|
33
40
|
out.writeString(opts.password);
|
|
34
41
|
}
|
|
35
42
|
out.writeInt8(0);
|
|
36
|
-
out.
|
|
43
|
+
out.flushPacket();
|
|
37
44
|
return;
|
|
38
45
|
} else {
|
|
39
46
|
// retrieve public key from configuration or from server
|
|
@@ -44,21 +51,18 @@ class Sha256PasswordAuth extends PluginAuth {
|
|
|
44
51
|
// rsaPublicKey contain path
|
|
45
52
|
key = fs.readFileSync(key, 'utf8');
|
|
46
53
|
}
|
|
47
|
-
this.publicKey = Sha256PasswordAuth.
|
|
54
|
+
this.publicKey = Sha256PasswordAuth.retrievePublicKey(key);
|
|
48
55
|
} catch (err) {
|
|
49
56
|
return this.throwError(err, info);
|
|
50
57
|
}
|
|
51
58
|
} else {
|
|
52
59
|
if (!opts.allowPublicKeyRetrieval) {
|
|
53
60
|
return this.throwError(
|
|
54
|
-
Errors.
|
|
61
|
+
Errors.createFatalError(
|
|
55
62
|
'RSA public key is not available client side. Either set option `rsaPublicKey` to indicate' +
|
|
56
63
|
' public key path, or allow public key retrieval with option `allowPublicKeyRetrieval`',
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
info,
|
|
60
|
-
'08S01',
|
|
61
|
-
Errors.ER_CANNOT_RETRIEVE_RSA_KEY
|
|
64
|
+
Errors.ER_CANNOT_RETRIEVE_RSA_KEY,
|
|
65
|
+
info
|
|
62
66
|
),
|
|
63
67
|
info
|
|
64
68
|
);
|
|
@@ -68,33 +72,21 @@ class Sha256PasswordAuth extends PluginAuth {
|
|
|
68
72
|
// ask public Key Retrieval
|
|
69
73
|
out.startPacket(this);
|
|
70
74
|
out.writeInt8(0x01);
|
|
71
|
-
out.
|
|
75
|
+
out.flushPacket();
|
|
72
76
|
return;
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
// send Sha256Password Packet
|
|
77
|
-
Sha256PasswordAuth.sendSha256PwdPacket(
|
|
78
|
-
this,
|
|
79
|
-
this.pluginData,
|
|
80
|
-
this.publicKey,
|
|
81
|
-
opts.password,
|
|
82
|
-
out
|
|
83
|
-
);
|
|
81
|
+
Sha256PasswordAuth.sendSha256PwdPacket(this, this.pluginData, this.publicKey, opts.password, out);
|
|
84
82
|
} else {
|
|
85
83
|
// has request public key
|
|
86
|
-
this.publicKey = Sha256PasswordAuth.
|
|
87
|
-
Sha256PasswordAuth.sendSha256PwdPacket(
|
|
88
|
-
this,
|
|
89
|
-
this.pluginData,
|
|
90
|
-
this.publicKey,
|
|
91
|
-
opts.password,
|
|
92
|
-
out
|
|
93
|
-
);
|
|
84
|
+
this.publicKey = Sha256PasswordAuth.retrievePublicKey(buffer.toString('utf8', 1));
|
|
85
|
+
Sha256PasswordAuth.sendSha256PwdPacket(this, this.pluginData, this.publicKey, opts.password, out);
|
|
94
86
|
}
|
|
95
87
|
}
|
|
96
88
|
|
|
97
|
-
static
|
|
89
|
+
static retrievePublicKey(key) {
|
|
98
90
|
return key.replace('(-+BEGIN PUBLIC KEY-+\\r?\\n|\\n?-+END PUBLIC KEY-+\\r?\\n?)', '');
|
|
99
91
|
}
|
|
100
92
|
|
|
@@ -103,7 +95,29 @@ class Sha256PasswordAuth extends PluginAuth {
|
|
|
103
95
|
out.startPacket(cmd);
|
|
104
96
|
const enc = Sha256PasswordAuth.encrypt(truncatedSeed, password, publicKey);
|
|
105
97
|
out.writeBuffer(enc, 0, enc.length);
|
|
106
|
-
out.
|
|
98
|
+
out.flushPacket();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static encryptSha256Password(password, seed) {
|
|
102
|
+
if (!password) return Buffer.alloc(0);
|
|
103
|
+
|
|
104
|
+
let hash = Crypto.createHash('sha256');
|
|
105
|
+
let stage1 = hash.update(password, 'utf8').digest();
|
|
106
|
+
hash = Crypto.createHash('sha256');
|
|
107
|
+
|
|
108
|
+
let stage2 = hash.update(stage1).digest();
|
|
109
|
+
hash = Crypto.createHash('sha256');
|
|
110
|
+
|
|
111
|
+
// order is different from sha 1 !!!!!
|
|
112
|
+
hash.update(stage2);
|
|
113
|
+
hash.update(seed);
|
|
114
|
+
|
|
115
|
+
let digest = hash.digest();
|
|
116
|
+
let returnBytes = Buffer.allocUnsafe(digest.length);
|
|
117
|
+
for (let i = 0; i < digest.length; i++) {
|
|
118
|
+
returnBytes[i] = stage1[i] ^ digest[i];
|
|
119
|
+
}
|
|
120
|
+
return returnBytes;
|
|
107
121
|
}
|
|
108
122
|
|
|
109
123
|
// encrypt password with public key
|
|
@@ -114,10 +128,7 @@ class Sha256PasswordAuth extends PluginAuth {
|
|
|
114
128
|
for (let i = 0; i < xorBytes.length; i++) {
|
|
115
129
|
xorBytes[i] = nullFinishedPwd[i] ^ seed[i % seedLength];
|
|
116
130
|
}
|
|
117
|
-
return crypto.publicEncrypt(
|
|
118
|
-
{ key: publicKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
|
|
119
|
-
xorBytes
|
|
120
|
-
);
|
|
131
|
+
return crypto.publicEncrypt({ key: publicKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }, xorBytes);
|
|
121
132
|
}
|
|
122
133
|
|
|
123
134
|
response(packet, out, opts, info) {
|
|
@@ -129,7 +140,7 @@ class Sha256PasswordAuth extends PluginAuth {
|
|
|
129
140
|
case 0x00:
|
|
130
141
|
case 0xff:
|
|
131
142
|
this.emit('send_end');
|
|
132
|
-
return this.
|
|
143
|
+
return this.multiAuthResolver(packet, out, opts, info);
|
|
133
144
|
|
|
134
145
|
default:
|
|
135
146
|
let promptData = packet.readBufferRemaining();
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
2
|
+
// Copyright (c) 2015-2025 MariaDB Corporation Ab
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const Command = require('../command');
|
|
7
|
+
const Errors = require('../../misc/errors');
|
|
8
|
+
const Capabilities = require('../../const/capabilities');
|
|
9
|
+
const Handshake = require('./auth/handshake');
|
|
10
|
+
const ServerStatus = require('../../const/server-status');
|
|
11
|
+
const StateChange = require('../../const/state-change');
|
|
12
|
+
const Collations = require('../../const/collations');
|
|
13
|
+
const Crypto = require('crypto');
|
|
14
|
+
const utils = require('../../misc/utils');
|
|
15
|
+
const tls = require('tls');
|
|
16
|
+
const authenticationPlugins = {
|
|
17
|
+
mysql_native_password: require('./auth/native-password-auth.js'),
|
|
18
|
+
mysql_clear_password: require('./auth/clear-password-auth'),
|
|
19
|
+
client_ed25519: require('./auth/ed25519-password-auth'),
|
|
20
|
+
parsec: require('./auth/parsec-auth'),
|
|
21
|
+
dialog: require('./auth/pam-password-auth'),
|
|
22
|
+
sha256_password: require('./auth/sha256-password-auth'),
|
|
23
|
+
caching_sha2_password: require('./auth/caching-sha2-password-auth')
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Handle handshake.
|
|
28
|
+
* see https://mariadb.com/kb/en/library/1-connecting-connecting/
|
|
29
|
+
*/
|
|
30
|
+
class Authentication extends Command {
|
|
31
|
+
constructor(cmdParam, resolve, reject, _createSecureContext, getSocket) {
|
|
32
|
+
super(cmdParam, resolve, reject);
|
|
33
|
+
this.cmdParam = cmdParam;
|
|
34
|
+
this._createSecureContext = _createSecureContext;
|
|
35
|
+
this.getSocket = getSocket;
|
|
36
|
+
this.plugin = new Handshake(this, getSocket, this.handshakeResult, reject);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
onPacketReceive(packet, out, opts, info) {
|
|
40
|
+
this.plugin.sequenceNo = this.sequenceNo;
|
|
41
|
+
this.plugin.compressSequenceNo = this.compressSequenceNo;
|
|
42
|
+
this.plugin.onPacketReceive(packet, out, opts, info);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Fast-path handshake results :
|
|
47
|
+
* - if plugin was the one expected by server, server will send OK_Packet / ERR_Packet.
|
|
48
|
+
* - if not, server send an AuthSwitchRequest packet, indicating the specific PLUGIN to use with this user.
|
|
49
|
+
* dispatching to plugin handler then.
|
|
50
|
+
*
|
|
51
|
+
* @param packet current packet
|
|
52
|
+
* @param out output buffer
|
|
53
|
+
* @param opts options
|
|
54
|
+
* @param info connection info
|
|
55
|
+
* @returns {*} return null if authentication succeed, depending on plugin conversation if not finished
|
|
56
|
+
*/
|
|
57
|
+
handshakeResult(packet, out, opts, info) {
|
|
58
|
+
const marker = packet.peek();
|
|
59
|
+
switch (marker) {
|
|
60
|
+
//*********************************************************************************************************
|
|
61
|
+
//* AuthSwitchRequest packet
|
|
62
|
+
//*********************************************************************************************************
|
|
63
|
+
case 0xfe:
|
|
64
|
+
this.dispatchAuthSwitchRequest(packet, out, opts, info);
|
|
65
|
+
return;
|
|
66
|
+
|
|
67
|
+
//*********************************************************************************************************
|
|
68
|
+
//* OK_Packet - authentication succeeded
|
|
69
|
+
//*********************************************************************************************************
|
|
70
|
+
case 0x00:
|
|
71
|
+
this.plugin.onPacketReceive = null;
|
|
72
|
+
packet.skip(1); //skip header
|
|
73
|
+
packet.skipLengthCodedNumber(); //skip affected rows
|
|
74
|
+
packet.skipLengthCodedNumber(); //skip last insert id
|
|
75
|
+
info.status = packet.readUInt16();
|
|
76
|
+
|
|
77
|
+
if (info.requireValidCert) {
|
|
78
|
+
if (info.selfSignedCertificate) {
|
|
79
|
+
// TLS was forced to trust, and certificate validation is required
|
|
80
|
+
packet.skip(2); //skip warning count
|
|
81
|
+
if (packet.remaining()) {
|
|
82
|
+
const validationHash = packet.readBufferLengthEncoded();
|
|
83
|
+
if (validationHash.length > 0) {
|
|
84
|
+
if (!this.plugin.permitHash() || !Boolean(this.cmdParam.opts.password)) {
|
|
85
|
+
return this.throwNewError(
|
|
86
|
+
'Self signed certificates. Either set `ssl: { rejectUnauthorized: false }` (trust mode) or provide server certificate to client',
|
|
87
|
+
true,
|
|
88
|
+
info,
|
|
89
|
+
'08000',
|
|
90
|
+
Errors.ER_SELF_SIGNED_NO_PWD
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (this.validateFingerPrint(validationHash, info)) {
|
|
94
|
+
return this.successEnd();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return this.throwNewError('self-signed certificate', true, info, '08000', Errors.ER_SELF_SIGNED);
|
|
99
|
+
} else {
|
|
100
|
+
// certificate is not self signed, validate server identity
|
|
101
|
+
const validationFunction =
|
|
102
|
+
opts.ssl === true || typeof opts.ssl.checkServerIdentity !== 'function'
|
|
103
|
+
? tls.checkServerIdentity
|
|
104
|
+
: opts.ssl.checkServerIdentity;
|
|
105
|
+
const identityError = validationFunction(
|
|
106
|
+
typeof opts.ssl === 'object' && opts.ssl.servername ? opts.ssl.servername : opts.host,
|
|
107
|
+
info.tlsCert
|
|
108
|
+
);
|
|
109
|
+
if (identityError) {
|
|
110
|
+
return this.throwNewError(
|
|
111
|
+
'certificate identify Error: ' + identityError.message,
|
|
112
|
+
true,
|
|
113
|
+
info,
|
|
114
|
+
'08000',
|
|
115
|
+
Errors.ER_TLS_IDENTITY_ERROR
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let mustRedirect = false;
|
|
122
|
+
if (info.status & ServerStatus.SESSION_STATE_CHANGED) {
|
|
123
|
+
packet.skip(2); //skip warning count
|
|
124
|
+
packet.skipLengthCodedNumber();
|
|
125
|
+
while (packet.remaining()) {
|
|
126
|
+
const len = packet.readUnsignedLength();
|
|
127
|
+
if (len > 0) {
|
|
128
|
+
const subPacket = packet.subPacketLengthEncoded(len);
|
|
129
|
+
while (subPacket.remaining()) {
|
|
130
|
+
const type = subPacket.readUInt8();
|
|
131
|
+
switch (type) {
|
|
132
|
+
case StateChange.SESSION_TRACK_SYSTEM_VARIABLES:
|
|
133
|
+
let subSubPacket;
|
|
134
|
+
do {
|
|
135
|
+
subSubPacket = subPacket.subPacketLengthEncoded(subPacket.readUnsignedLength());
|
|
136
|
+
const variable = subSubPacket.readStringLengthEncoded();
|
|
137
|
+
const value = subSubPacket.readStringLengthEncoded();
|
|
138
|
+
|
|
139
|
+
switch (variable) {
|
|
140
|
+
case 'character_set_client':
|
|
141
|
+
info.collation = Collations.fromCharset(value);
|
|
142
|
+
if (info.collation === undefined) {
|
|
143
|
+
this.throwError(new Error("unknown charset : '" + value + "'"), info);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
opts.emit('collation', info.collation);
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case 'redirect_url':
|
|
150
|
+
if (value !== '') {
|
|
151
|
+
mustRedirect = true;
|
|
152
|
+
info.redirect(value, this.successEnd.bind(this));
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
|
|
156
|
+
case 'maxscale':
|
|
157
|
+
info.maxscaleVersion = value;
|
|
158
|
+
break;
|
|
159
|
+
|
|
160
|
+
case 'connection_id':
|
|
161
|
+
info.threadId = parseInt(value);
|
|
162
|
+
break;
|
|
163
|
+
|
|
164
|
+
default:
|
|
165
|
+
//variable not used by driver
|
|
166
|
+
}
|
|
167
|
+
} while (subSubPacket.remaining() > 0);
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case StateChange.SESSION_TRACK_SCHEMA:
|
|
171
|
+
const subSubPacket2 = subPacket.subPacketLengthEncoded(subPacket.readUnsignedLength());
|
|
172
|
+
info.database = subSubPacket2.readStringLengthEncoded();
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!mustRedirect) this.successEnd();
|
|
180
|
+
return;
|
|
181
|
+
|
|
182
|
+
//*********************************************************************************************************
|
|
183
|
+
//* ERR_Packet
|
|
184
|
+
//*********************************************************************************************************
|
|
185
|
+
case 0xff:
|
|
186
|
+
this.plugin.onPacketReceive = null;
|
|
187
|
+
const authErr = packet.readError(info, this.displaySql(), undefined);
|
|
188
|
+
authErr.fatal = true;
|
|
189
|
+
if (info.requireValidCert && info.selfSignedCertificate) {
|
|
190
|
+
// TLS was forced to trust, and certificate validation is required
|
|
191
|
+
return this.plugin.throwNewError(
|
|
192
|
+
'Self signed certificates. Either set `ssl: { rejectUnauthorized: false }` (trust mode) or provide server certificate to client',
|
|
193
|
+
true,
|
|
194
|
+
info,
|
|
195
|
+
'08000',
|
|
196
|
+
Errors.ER_SELF_SIGNED_NO_PWD
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
return this.plugin.throwError(authErr, info);
|
|
200
|
+
|
|
201
|
+
//*********************************************************************************************************
|
|
202
|
+
//* unexpected
|
|
203
|
+
//*********************************************************************************************************
|
|
204
|
+
default:
|
|
205
|
+
this.throwNewError(
|
|
206
|
+
`Unexpected type of packet during handshake phase : ${marker}`,
|
|
207
|
+
true,
|
|
208
|
+
info,
|
|
209
|
+
'42000',
|
|
210
|
+
Errors.ER_AUTHENTICATION_BAD_PACKET
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
validateFingerPrint(validationHash, info) {
|
|
216
|
+
if (validationHash.length === 0 || !info.tlsFingerprint) return false;
|
|
217
|
+
|
|
218
|
+
// 0x01 = SHA256 encryption
|
|
219
|
+
if (validationHash[0] !== 0x01) {
|
|
220
|
+
const err = Errors.createFatalError(
|
|
221
|
+
`Unexpected hash format for fingerprint hash encoding`,
|
|
222
|
+
Errors.ER_UNEXPECTED_PACKET,
|
|
223
|
+
this.info
|
|
224
|
+
);
|
|
225
|
+
if (this.opts.logger.error) this.opts.logger.error(err);
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const pwdHash = this.plugin.hash(this.cmdParam.opts);
|
|
230
|
+
|
|
231
|
+
let hash = Crypto.createHash('sha256');
|
|
232
|
+
let digest = hash.update(pwdHash).update(info.seed).update(Buffer.from(info.tlsFingerprint, 'hex')).digest();
|
|
233
|
+
const hashHex = utils.toHexString(digest);
|
|
234
|
+
const serverValidationHex = validationHash.toString('ascii', 1, validationHash.length).toLowerCase();
|
|
235
|
+
return hashHex === serverValidationHex;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Handle authentication switch request : dispatch to plugin handler.
|
|
240
|
+
*
|
|
241
|
+
* @param packet packet
|
|
242
|
+
* @param out output writer
|
|
243
|
+
* @param opts options
|
|
244
|
+
* @param info connection information
|
|
245
|
+
*/
|
|
246
|
+
dispatchAuthSwitchRequest(packet, out, opts, info) {
|
|
247
|
+
let pluginName, pluginData;
|
|
248
|
+
if (info.clientCapabilities & Capabilities.PLUGIN_AUTH) {
|
|
249
|
+
packet.skip(1); //header
|
|
250
|
+
if (packet.remaining()) {
|
|
251
|
+
//AuthSwitchRequest packet.
|
|
252
|
+
pluginName = packet.readStringNullEnded();
|
|
253
|
+
pluginData = packet.readBufferRemaining();
|
|
254
|
+
} else {
|
|
255
|
+
//OldAuthSwitchRequest
|
|
256
|
+
pluginName = 'mysql_old_password';
|
|
257
|
+
pluginData = info.seed.subarray(0, 8);
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
pluginName = packet.readStringNullEnded('ascii');
|
|
261
|
+
pluginData = packet.readBufferRemaining();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (
|
|
265
|
+
info.requireValidCert &&
|
|
266
|
+
info.selfSignedCertificate &&
|
|
267
|
+
Boolean(this.cmdParam.opts.password) &&
|
|
268
|
+
!this.plugin.permitHash()
|
|
269
|
+
) {
|
|
270
|
+
return this.throwNewError(
|
|
271
|
+
`Unsupported authentication plugin ${pluginName} with Self signed certificates. Either set 'ssl: { rejectUnauthorized: false }' (trust mode) or provide server certificate to client`,
|
|
272
|
+
true,
|
|
273
|
+
info,
|
|
274
|
+
'08000',
|
|
275
|
+
Errors.ER_SELF_SIGNED_BAD_PLUGIN
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (opts.restrictedAuth && !opts.restrictedAuth.includes(pluginName)) {
|
|
280
|
+
this.throwNewError(
|
|
281
|
+
`Unsupported authentication plugin ${pluginName}. Authorized plugin: ${opts.restrictedAuth.toString()}`,
|
|
282
|
+
true,
|
|
283
|
+
info,
|
|
284
|
+
'42000',
|
|
285
|
+
Errors.ER_NOT_SUPPORTED_AUTH_PLUGIN
|
|
286
|
+
);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
this.plugin.emit('end');
|
|
291
|
+
this.plugin.onPacketReceive = null;
|
|
292
|
+
this.plugin = Authentication.pluginHandler(
|
|
293
|
+
pluginName,
|
|
294
|
+
this.plugin.sequenceNo,
|
|
295
|
+
this.plugin.compressSequenceNo,
|
|
296
|
+
pluginData,
|
|
297
|
+
info,
|
|
298
|
+
opts,
|
|
299
|
+
out,
|
|
300
|
+
this.cmdParam,
|
|
301
|
+
this.reject,
|
|
302
|
+
this.handshakeResult.bind(this)
|
|
303
|
+
);
|
|
304
|
+
this.plugin.start(out, opts, info);
|
|
305
|
+
} catch (err) {
|
|
306
|
+
this.reject(err);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
static pluginHandler(
|
|
311
|
+
pluginName,
|
|
312
|
+
packSeq,
|
|
313
|
+
compressPackSeq,
|
|
314
|
+
pluginData,
|
|
315
|
+
info,
|
|
316
|
+
opts,
|
|
317
|
+
out,
|
|
318
|
+
cmdParam,
|
|
319
|
+
authReject,
|
|
320
|
+
multiAuthResolver
|
|
321
|
+
) {
|
|
322
|
+
let pluginAuth = authenticationPlugins[pluginName];
|
|
323
|
+
if (!pluginAuth) {
|
|
324
|
+
throw Errors.createFatalError(
|
|
325
|
+
`Client does not support authentication protocol '${pluginName}' requested by server.`,
|
|
326
|
+
Errors.ER_AUTHENTICATION_PLUGIN_NOT_SUPPORTED,
|
|
327
|
+
info,
|
|
328
|
+
'08004'
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
return new pluginAuth(packSeq, compressPackSeq, pluginData, cmdParam, authReject, multiAuthResolver);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
module.exports = Authentication;
|