Skip to content

Commit f4d8451

Browse files
committed
fixup: make MD5 computation pluggable
1 parent e5ab7c5 commit f4d8451

File tree

3 files changed

+50
-28
lines changed

3 files changed

+50
-28
lines changed

packages/service-provider-core/src/admin.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ export default interface Admin {
6666
*/
6767
bsonLibrary: BSON;
6868

69+
/**
70+
* Compute a hex MD5 hash from a string. Used for legacy auth mechanisms such as
71+
* SCRAM-SHA-1.
72+
*/
73+
computeLegacyHexMD5?(str: string): Promise<string>;
74+
6975
/**
7076
* list databases.
7177
*

packages/shell-api/src/database.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -581,10 +581,11 @@ export class Database<
581581
if (writeConcern) {
582582
command.writeConcern = writeConcern;
583583
}
584-
const digestPwd = processDigestPassword(
584+
const digestPwd = await processDigestPassword(
585585
user.user,
586586
user.passwordDigestor,
587-
command
587+
command,
588+
this._instanceState.currentServiceProvider
588589
);
589590
const orderedCmd = {
590591
createUser: command.createUser,
@@ -627,10 +628,11 @@ export class Database<
627628
if (writeConcern) {
628629
command.writeConcern = writeConcern;
629630
}
630-
const digestPwd = processDigestPassword(
631+
const digestPwd = await processDigestPassword(
631632
username,
632633
userDoc.passwordDigestor,
633-
command
634+
command,
635+
this._instanceState.currentServiceProvider
634636
);
635637
const orderedCmd = {
636638
updateUser: command.updateUser,

packages/shell-api/src/helpers.ts

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
DeleteOptions,
1010
MapReduceOptions,
1111
ExplainOptions,
12+
ServiceProvider,
1213
} from '@mongosh/service-provider-core';
1314
import {
1415
CommonErrors,
@@ -176,6 +177,35 @@ export function adaptOptions(
176177
}, additions);
177178
}
178179

180+
async function computeLegacyHexMD5(
181+
sp: ServiceProvider,
182+
str: string
183+
): Promise<string> {
184+
const digested = await sp.computeLegacyHexMD5?.(str);
185+
if (digested) return digested;
186+
187+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
188+
let crypto: typeof import('crypto');
189+
try {
190+
// Try to dynamically import crypto so that we don't break plain-JS-runtime builds.
191+
// The Web Crypto API does not provide MD5, which is reasonable for a modern API
192+
// but means that we cannot use it as a fallback.
193+
crypto = require('crypto');
194+
} catch {
195+
throw new MongoshUnimplementedError(
196+
'Legacy password digest algorithms like SCRAM-SHA-1 are not supported by this instance of mongosh',
197+
CommonErrors.Deprecated
198+
);
199+
}
200+
// NOTE: this code has raised a code scanning alert about the "use of a broken or weak cryptographic algorithm":
201+
// we inherited this code from `mongo`, and we cannot replace MD5 with a different algorithm, since MD5 is part of the SCRAM-SHA-1 protocol,
202+
// and the purpose of `passwordDigestor=client` is to improve the security of SCRAM-SHA-1, allowing the creation of new users
203+
// without the need to communicate their password to the server.
204+
const hash = crypto.createHash('md5');
205+
hash.update(str);
206+
return hash.digest('hex');
207+
}
208+
179209
/**
180210
* Optionally digest password if passwordDigestor field set to 'client'. If it's false,
181211
* then hash the password.
@@ -184,11 +214,12 @@ export function adaptOptions(
184214
* @param passwordDigestor
185215
* @param {Object} command
186216
*/
187-
export function processDigestPassword(
217+
export async function processDigestPassword(
188218
username: string,
189219
passwordDigestor: 'server' | 'client',
190-
command: { pwd: string }
191-
): { digestPassword?: boolean; pwd?: string } {
220+
command: { pwd: string },
221+
sp: ServiceProvider
222+
): Promise<{ digestPassword?: boolean; pwd?: string }> {
192223
if (passwordDigestor === undefined) {
193224
return {};
194225
}
@@ -205,27 +236,10 @@ export function processDigestPassword(
205236
CommonErrors.InvalidArgument
206237
);
207238
}
208-
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
209-
let crypto: typeof import('crypto');
210-
try {
211-
// Try to dynamically import crypto so that we don't break plain-JS-runtime builds.
212-
// The Web Crypto API does not provide MD5, which is reasonable for a modern API
213-
// but means that we cannot use it as a fallback.
214-
crypto = require('crypto');
215-
} catch {
216-
throw new MongoshUnimplementedError(
217-
'Legacy password digest algorithms like SCRAM-SHA-1 are not supported by this instance of mongosh',
218-
CommonErrors.Deprecated
219-
);
220-
}
221-
// NOTE: this code has raised a code scanning alert about the "use of a broken or weak cryptographic algorithm":
222-
// we inherited this code from `mongo`, and we cannot replace MD5 with a different algorithm, since MD5 is part of the SCRAM-SHA-1 protocol,
223-
// and the purpose of `passwordDigestor=client` is to improve the security of SCRAM-SHA-1, allowing the creation of new users
224-
// without the need to communicate their password to the server.
225-
const hash = crypto.createHash('md5');
226-
hash.update(`${username}:mongo:${command.pwd}`);
227-
const digested = hash.digest('hex');
228-
return { digestPassword: false, pwd: digested };
239+
return {
240+
digestPassword: false,
241+
pwd: await computeLegacyHexMD5(sp, `${username}:mongo:${command.pwd}`),
242+
};
229243
}
230244
return { digestPassword: true };
231245
}

0 commit comments

Comments
 (0)