mongodb-core
Advanced tools
Comparing version 3.1.0-beta2 to 3.1.0-beta3
@@ -31,2 +31,3 @@ 'use strict'; | ||
// Auth mechanisms | ||
defaultAuthProviders: require('./lib/auth/defaultAuthProviders').defaultAuthProviders, | ||
MongoCR: require('./lib/auth/mongocr'), | ||
@@ -36,5 +37,6 @@ X509: require('./lib/auth/x509'), | ||
GSSAPI: require('./lib/auth/gssapi'), | ||
ScramSHA1: require('./lib/auth/scram'), | ||
ScramSHA1: require('./lib/auth/scram').ScramSHA1, | ||
ScramSHA256: require('./lib/auth/scram').ScramSHA256, | ||
// Utilities | ||
parseConnectionString: require('./lib/uri_parser') | ||
}; |
@@ -9,2 +9,10 @@ 'use strict'; | ||
let saslprep; | ||
try { | ||
saslprep = require('saslprep'); | ||
} catch (e) { | ||
// don't do anything; | ||
} | ||
var BSON = retrieveBSON(), | ||
@@ -30,10 +38,11 @@ Binary = BSON.Binary; | ||
/** | ||
* Creates a new ScramSHA1 authentication mechanism | ||
* Creates a new ScramSHA authentication mechanism | ||
* @class | ||
* @return {ScramSHA1} A cursor instance | ||
* @return {ScramSHA} A cursor instance | ||
*/ | ||
var ScramSHA1 = function(bson) { | ||
var ScramSHA = function(bson, cryptoMethod) { | ||
this.bson = bson; | ||
this.authStore = []; | ||
this.id = id++; | ||
this.cryptoMethod = cryptoMethod || 'sha1'; | ||
}; | ||
@@ -65,18 +74,29 @@ | ||
// XOR two buffers | ||
var xor = function(a, b) { | ||
function xor(a, b) { | ||
if (!Buffer.isBuffer(a)) a = new Buffer(a); | ||
if (!Buffer.isBuffer(b)) b = new Buffer(b); | ||
var res = []; | ||
if (a.length > b.length) { | ||
for (var i = 0; i < b.length; i++) { | ||
res.push(a[i] ^ b[i]); | ||
} | ||
} else { | ||
for (i = 0; i < a.length; i++) { | ||
res.push(a[i] ^ b[i]); | ||
} | ||
const length = Math.max(a.length, b.length); | ||
const res = []; | ||
for (let i = 0; i < length; i += 1) { | ||
res.push(a[i] ^ b[i]); | ||
} | ||
return new Buffer(res); | ||
}; | ||
return new Buffer(res).toString('base64'); | ||
} | ||
function H(method, text) { | ||
return crypto | ||
.createHash(method) | ||
.update(text) | ||
.digest(); | ||
} | ||
function HMAC(method, key, text) { | ||
return crypto | ||
.createHmac(method, key) | ||
.update(text) | ||
.digest(); | ||
} | ||
var _hiCache = {}; | ||
@@ -89,5 +109,10 @@ var _hiCacheCount = 0; | ||
var hi = function(data, salt, iterations) { | ||
const hiLengthMap = { | ||
sha256: 32, | ||
sha1: 20 | ||
}; | ||
function HI(data, salt, iterations, cryptoMethod) { | ||
// omit the work if already generated | ||
var key = [data, salt.toString('base64'), iterations].join('_'); | ||
const key = [data, salt.toString('base64'), iterations].join('_'); | ||
if (_hiCache[key] !== undefined) { | ||
@@ -98,3 +123,9 @@ return _hiCache[key]; | ||
// generate the salt | ||
var saltedData = crypto.pbkdf2Sync(data, salt, iterations, 20, 'sha1'); | ||
const saltedData = crypto.pbkdf2Sync( | ||
data, | ||
salt, | ||
iterations, | ||
hiLengthMap[cryptoMethod], | ||
cryptoMethod | ||
); | ||
@@ -109,3 +140,3 @@ // cache a copy to speed up the next lookup, but prevent unbounded cache growth | ||
return saltedData; | ||
}; | ||
} | ||
@@ -123,3 +154,3 @@ /** | ||
*/ | ||
ScramSHA1.prototype.auth = function(server, connections, db, username, password, callback) { | ||
ScramSHA.prototype.auth = function(server, connections, db, username, password, callback) { | ||
var self = this; | ||
@@ -134,2 +165,21 @@ // Total connections | ||
const cryptoMethod = this.cryptoMethod; | ||
let mechanism = 'SCRAM-SHA-1'; | ||
let processedPassword; | ||
if (cryptoMethod === 'sha256') { | ||
mechanism = 'SCRAM-SHA-256'; | ||
let saslprepFn = (server.s && server.s.saslprep) || saslprep; | ||
if (saslprepFn) { | ||
processedPassword = saslprepFn(password); | ||
} else { | ||
console.warn('Warning: no saslprep library specified. Passwords will not be sanitized'); | ||
processedPassword = password; | ||
} | ||
} else { | ||
processedPassword = passwordDigest(username, password); | ||
} | ||
// Execute MongoCR | ||
@@ -143,9 +193,17 @@ var executeScram = function(connection) { | ||
// var nonce = 'MsQUY9iw0T9fx2MUEz6LZPwGuhVvWAhc' | ||
var firstBare = f('n=%s,r=%s', username, nonce); | ||
// NOTE: This is done b/c Javascript uses UTF-16, but the server is hashing in UTF-8. | ||
// Since the username is not sasl-prep-d, we need to do this here. | ||
const firstBare = Buffer.concat([ | ||
Buffer.from('n=', 'utf8'), | ||
Buffer.from(username, 'utf8'), | ||
Buffer.from(',r=', 'utf8'), | ||
Buffer.from(nonce, 'utf8') | ||
]); | ||
// Build command structure | ||
var cmd = { | ||
saslStart: 1, | ||
mechanism: 'SCRAM-SHA-1', | ||
payload: new Binary(f('n,,%s', firstBare)), | ||
mechanism: mechanism, | ||
payload: new Binary(Buffer.concat([Buffer.from('n,,', 'utf8'), firstBare])), | ||
autoAuthorize: 1 | ||
@@ -232,34 +290,38 @@ }; | ||
var withoutProof = f('c=biws,r=%s', rnonce); | ||
var passwordDig = passwordDigest(username, password); | ||
var saltedPassword = hi(passwordDig, new Buffer(salt, 'base64'), iterations); | ||
var saltedPassword = HI( | ||
processedPassword, | ||
new Buffer(salt, 'base64'), | ||
iterations, | ||
cryptoMethod | ||
); | ||
if (iterations && iterations < 4096) { | ||
const error = new MongoError(`Server returned an invalid iteration count ${iterations}`); | ||
return callback(error, false); | ||
} | ||
// Create the client key | ||
var hmac = crypto.createHmac('sha1', saltedPassword); | ||
hmac.update(new Buffer('Client Key')); | ||
var clientKey = new Buffer(hmac.digest('base64'), 'base64'); | ||
const clientKey = HMAC(cryptoMethod, saltedPassword, 'Client Key'); | ||
// Create the stored key | ||
var hash = crypto.createHash('sha1'); | ||
hash.update(clientKey); | ||
var storedKey = new Buffer(hash.digest('base64'), 'base64'); | ||
const storedKey = H(cryptoMethod, clientKey); | ||
// Create the authentication message | ||
var authMsg = [firstBare, r.result.payload.value().toString('base64'), withoutProof].join( | ||
',' | ||
); | ||
const authMessage = [ | ||
firstBare, | ||
r.result.payload.value().toString('base64'), | ||
withoutProof | ||
].join(','); | ||
// Create client signature | ||
hmac = crypto.createHmac('sha1', storedKey); | ||
hmac.update(new Buffer(authMsg)); | ||
var clientSig = new Buffer(hmac.digest('base64'), 'base64'); | ||
const clientSignature = HMAC(cryptoMethod, storedKey, authMessage); | ||
// Create client proof | ||
var clientProof = f('p=%s', new Buffer(xor(clientKey, clientSig)).toString('base64')); | ||
const clientProof = f('p=%s', xor(clientKey, clientSignature)); | ||
// Create client final | ||
var clientFinal = [withoutProof, clientProof].join(','); | ||
const clientFinal = [withoutProof, clientProof].join(','); | ||
// | ||
// Create continue message | ||
var cmd = { | ||
const cmd = { | ||
saslContinue: 1, | ||
@@ -339,3 +401,3 @@ conversationId: r.result.conversationId, | ||
*/ | ||
ScramSHA1.prototype.logout = function(dbName) { | ||
ScramSHA.prototype.logout = function(dbName) { | ||
this.authStore = this.authStore.filter(function(x) { | ||
@@ -354,3 +416,3 @@ return x.db !== dbName; | ||
*/ | ||
ScramSHA1.prototype.reauthenticate = function(server, connections, callback) { | ||
ScramSHA.prototype.reauthenticate = function(server, connections, callback) { | ||
var authStore = this.authStore.slice(0); | ||
@@ -379,2 +441,14 @@ var count = authStore.length; | ||
module.exports = ScramSHA1; | ||
class ScramSHA1 extends ScramSHA { | ||
constructor(bson) { | ||
super(bson, 'sha1'); | ||
} | ||
} | ||
class ScramSHA256 extends ScramSHA { | ||
constructor(bson) { | ||
super(bson, 'sha256'); | ||
} | ||
} | ||
module.exports = { ScramSHA1, ScramSHA256 }; |
@@ -21,8 +21,3 @@ 'use strict'; | ||
var MongoCR = require('../auth/mongocr'), | ||
X509 = require('../auth/x509'), | ||
Plain = require('../auth/plain'), | ||
GSSAPI = require('../auth/gssapi'), | ||
SSPI = require('../auth/sspi'), | ||
ScramSHA1 = require('../auth/scram'); | ||
const defaultAuthProviders = require('../auth/defaultAuthProviders').defaultAuthProviders; | ||
@@ -151,10 +146,3 @@ var DISCONNECTED = 'disconnected'; | ||
// All the authProviders | ||
this.authProviders = options.authProviders || { | ||
mongocr: new MongoCR(options.bson), | ||
x509: new X509(options.bson), | ||
plain: new Plain(options.bson), | ||
gssapi: new GSSAPI(options.bson), | ||
sspi: new SSPI(options.bson), | ||
'scram-sha-1': new ScramSHA1(options.bson) | ||
}; | ||
this.authProviders = options.authProviders || defaultAuthProviders(options.bson); | ||
@@ -829,3 +817,3 @@ // Contains the reconnect connection | ||
for (var i = 0; i < connections.length; i++) { | ||
authenticate(self, args, connections[i], function(err) { | ||
authenticate(self, args, connections[i], function(err, result) { | ||
connectionsCount = connectionsCount - 1; | ||
@@ -856,5 +844,5 @@ | ||
return cb(error); | ||
return cb(error, result); | ||
} | ||
cb(null); | ||
cb(null, result); | ||
} | ||
@@ -876,3 +864,3 @@ }); | ||
// Authenticate all live connections | ||
authenticateLiveConnections(self, args, function(err) { | ||
authenticateLiveConnections(self, args, function(err, result) { | ||
// Credentials correctly stored in auth provider if successful | ||
@@ -882,3 +870,3 @@ // Any new connections will now reauthenticate correctly | ||
// Return after authentication connections | ||
callback(err); | ||
callback(err, result); | ||
}); | ||
@@ -1231,3 +1219,7 @@ }); | ||
if (command instanceof Query) { | ||
Object.assign(command.query, sessionOptions); | ||
if (command.query.$query) { | ||
Object.assign(command.query.$query, sessionOptions); | ||
} else { | ||
Object.assign(command.query, sessionOptions); | ||
} | ||
} else { | ||
@@ -1234,0 +1226,0 @@ Object.assign(command, sessionOptions); |
@@ -40,8 +40,3 @@ 'use strict'; | ||
var MongoCR = require('../auth/mongocr'), | ||
X509 = require('../auth/x509'), | ||
Plain = require('../auth/plain'), | ||
GSSAPI = require('../auth/gssapi'), | ||
SSPI = require('../auth/sspi'), | ||
ScramSHA1 = require('../auth/scram'); | ||
const defaultAuthProviders = require('../auth/defaultAuthProviders').defaultAuthProviders; | ||
@@ -204,10 +199,3 @@ // | ||
// All the authProviders | ||
this.authProviders = options.authProviders || { | ||
mongocr: new MongoCR(this.s.bson), | ||
x509: new X509(this.s.bson), | ||
plain: new Plain(this.s.bson), | ||
gssapi: new GSSAPI(this.s.bson), | ||
sspi: new SSPI(this.s.bson), | ||
'scram-sha-1': new ScramSHA1(this.s.bson) | ||
}; | ||
this.authProviders = options.authProviders || defaultAuthProviders(this.s.bson); | ||
@@ -214,0 +202,0 @@ // Disconnected state |
@@ -22,8 +22,3 @@ 'use strict'; | ||
var MongoCR = require('../auth/mongocr'), | ||
X509 = require('../auth/x509'), | ||
Plain = require('../auth/plain'), | ||
GSSAPI = require('../auth/gssapi'), | ||
SSPI = require('../auth/sspi'), | ||
ScramSHA1 = require('../auth/scram'); | ||
const defaultAuthProviders = require('../auth/defaultAuthProviders').defaultAuthProviders; | ||
@@ -227,10 +222,3 @@ var BSON = retrieveBSON(); | ||
// All the authProviders | ||
this.authProviders = options.authProviders || { | ||
mongocr: new MongoCR(this.s.bson), | ||
x509: new X509(this.s.bson), | ||
plain: new Plain(this.s.bson), | ||
gssapi: new GSSAPI(this.s.bson), | ||
sspi: new SSPI(this.s.bson), | ||
'scram-sha-1': new ScramSHA1(this.s.bson) | ||
}; | ||
this.authProviders = options.authProviders || defaultAuthProviders(this.s.bson); | ||
@@ -237,0 +225,0 @@ // Add forwarding of events from state handler |
@@ -24,2 +24,53 @@ 'use strict'; | ||
function getSaslSupportedMechs(options) { | ||
if (!options) { | ||
return {}; | ||
} | ||
const authArray = options.auth || []; | ||
const authMechanism = authArray[0] || options.authMechanism; | ||
const authSource = authArray[1] || options.authSource || options.dbName || 'admin'; | ||
const user = authArray[2] || options.user; | ||
if (typeof authMechanism === 'string' && authMechanism.toUpperCase() !== 'DEFAULT') { | ||
return {}; | ||
} | ||
if (!user) { | ||
return {}; | ||
} | ||
return { saslSupportedMechs: `${authSource}.${user}` }; | ||
} | ||
function getDefaultAuthMechanism(ismaster) { | ||
if (ismaster) { | ||
// If ismaster contains saslSupportedMechs, use scram-sha-256 | ||
// if it is available, else scram-sha-1 | ||
if (Array.isArray(ismaster.saslSupportedMechs)) { | ||
return ismaster.saslSupportedMechs.indexOf('SCRAM-SHA-256') >= 0 | ||
? 'scram-sha-256' | ||
: 'scram-sha-1'; | ||
} | ||
// Fallback to legacy selection method. If wire version >= 3, use scram-sha-1 | ||
if (ismaster.maxWireVersion >= 3) { | ||
return 'scram-sha-1'; | ||
} | ||
} | ||
// Default for wireprotocol < 3 | ||
return 'mongocr'; | ||
} | ||
function extractIsMasterError(err, result) { | ||
if (err) { | ||
return err; | ||
} | ||
if (result && result.result && result.result.ok === 0) { | ||
return new MongoError(result.result); | ||
} | ||
} | ||
// Used for filtering out fields for loggin | ||
@@ -348,3 +399,6 @@ var debugFields = [ | ||
'admin.$cmd', | ||
{ ismaster: true, client: self.clientInfo, compression: compressors }, | ||
Object.assign( | ||
{ ismaster: true, client: self.clientInfo, compression: compressors }, | ||
getSaslSupportedMechs(self.s.options) | ||
), | ||
queryOptions | ||
@@ -363,5 +417,8 @@ ); | ||
self.lastIsMasterMS = new Date().getTime() - start; | ||
if (err) { | ||
const serverError = extractIsMasterError(err, result); | ||
if (serverError) { | ||
self.destroy(); | ||
return self.emit('error', err); | ||
return self.emit('error', serverError); | ||
} | ||
@@ -899,8 +956,4 @@ | ||
// If we have the default mechanism we pick mechanism based on the wire | ||
// protocol max version. If it's >= 3 then scram-sha1 otherwise mongodb-cr | ||
if (mechanism === 'default' && self.ismaster && self.ismaster.maxWireVersion >= 3) { | ||
mechanism = 'scram-sha-1'; | ||
} else if (mechanism === 'default') { | ||
mechanism = 'mongocr'; | ||
if (mechanism === 'default') { | ||
mechanism = getDefaultAuthMechanism(self.ismaster); | ||
} | ||
@@ -907,0 +960,0 @@ |
{ | ||
"name": "mongodb-core", | ||
"version": "3.1.0-beta2", | ||
"version": "3.1.0-beta3", | ||
"description": "Core MongoDB driver functionality, no bells and whistles and meant for integration not end applications", | ||
@@ -47,3 +47,6 @@ "main": "index.js", | ||
}, | ||
"homepage": "https://github.com/mongodb-js/mongodb-core" | ||
"homepage": "https://github.com/mongodb-js/mongodb-core", | ||
"optionalDependencies": { | ||
"saslprep": "^1.0.0" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
45
12508
524526
3