"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveCname = exports.performGSSAPICanonicalizeHostName = exports.GSSAPI = exports.GSSAPICanonicalizationValue = void 0; const dns = require("dns"); const deps_1 = require("../../deps"); const error_1 = require("../../error"); const utils_1 = require("../../utils"); const auth_provider_1 = require("./auth_provider"); /** @public */ exports.GSSAPICanonicalizationValue = Object.freeze({ on: true, off: false, none: 'none', forward: 'forward', forwardAndReverse: 'forwardAndReverse' }); class GSSAPI extends auth_provider_1.AuthProvider { auth(authContext, callback) { const { connection, credentials } = authContext; if (credentials == null) return callback(new error_1.MongoMissingCredentialsError('Credentials required for GSSAPI authentication')); const { username } = credentials; function externalCommand(command, cb) { return connection.command((0, utils_1.ns)('$external.$cmd'), command, undefined, cb); } makeKerberosClient(authContext, (err, client) => { if (err) return callback(err); if (client == null) return callback(new error_1.MongoMissingDependencyError('GSSAPI client missing')); client.step('', (err, payload) => { if (err) return callback(err); externalCommand(saslStart(payload), (err, result) => { if (err) return callback(err); if (result == null) return callback(); negotiate(client, 10, result.payload, (err, payload) => { if (err) return callback(err); externalCommand(saslContinue(payload, result.conversationId), (err, result) => { if (err) return callback(err); if (result == null) return callback(); finalize(client, username, result.payload, (err, payload) => { if (err) return callback(err); externalCommand({ saslContinue: 1, conversationId: result.conversationId, payload }, (err, result) => { if (err) return callback(err); callback(undefined, result); }); }); }); }); }); }); }); } } exports.GSSAPI = GSSAPI; function makeKerberosClient(authContext, callback) { var _a; const { hostAddress } = authContext.options; const { credentials } = authContext; if (!hostAddress || typeof hostAddress.host !== 'string' || !credentials) { return callback(new error_1.MongoInvalidArgumentError('Connection must have host and port and credentials defined.')); } if ('kModuleError' in deps_1.Kerberos) { return callback(deps_1.Kerberos['kModuleError']); } const { initializeClient } = deps_1.Kerberos; const { username, password } = credentials; const mechanismProperties = credentials.mechanismProperties; const serviceName = (_a = mechanismProperties.SERVICE_NAME) !== null && _a !== void 0 ? _a : 'mongodb'; performGSSAPICanonicalizeHostName(hostAddress.host, mechanismProperties, (err, host) => { var _a; if (err) return callback(err); const initOptions = {}; if (password != null) { Object.assign(initOptions, { user: username, password: password }); } const spnHost = (_a = mechanismProperties.SERVICE_HOST) !== null && _a !== void 0 ? _a : host; let spn = `${serviceName}${process.platform === 'win32' ? '/' : '@'}${spnHost}`; if ('SERVICE_REALM' in mechanismProperties) { spn = `${spn}@${mechanismProperties.SERVICE_REALM}`; } initializeClient(spn, initOptions, (err, client) => { // TODO(NODE-3483) if (err) return callback(new error_1.MongoRuntimeError(err)); callback(undefined, client); }); }); } function saslStart(payload) { return { saslStart: 1, mechanism: 'GSSAPI', payload, autoAuthorize: 1 }; } function saslContinue(payload, conversationId) { return { saslContinue: 1, conversationId, payload }; } function negotiate(client, retries, payload, callback) { client.step(payload, (err, response) => { // Retries exhausted, raise error if (err && retries === 0) return callback(err); // Adjust number of retries and call step again if (err) return negotiate(client, retries - 1, payload, callback); // Return the payload callback(undefined, response || ''); }); } function finalize(client, user, payload, callback) { // GSS Client Unwrap client.unwrap(payload, (err, response) => { if (err) return callback(err); // Wrap the response client.wrap(response || '', { user }, (err, wrapped) => { if (err) return callback(err); // Return the payload callback(undefined, wrapped); }); }); } function performGSSAPICanonicalizeHostName(host, mechanismProperties, callback) { const mode = mechanismProperties.CANONICALIZE_HOST_NAME; if (!mode || mode === exports.GSSAPICanonicalizationValue.none) { return callback(undefined, host); } // If forward and reverse or true if (mode === exports.GSSAPICanonicalizationValue.on || mode === exports.GSSAPICanonicalizationValue.forwardAndReverse) { // Perform the lookup of the ip address. dns.lookup(host, (error, address) => { // No ip found, return the error. if (error) return callback(error); // Perform a reverse ptr lookup on the ip address. dns.resolvePtr(address, (err, results) => { // This can error as ptr records may not exist for all ips. In this case // fallback to a cname lookup as dns.lookup() does not return the // cname. if (err) { return resolveCname(host, callback); } // If the ptr did not error but had no results, return the host. callback(undefined, results.length > 0 ? results[0] : host); }); }); } else { // The case for forward is just to resolve the cname as dns.lookup() // will not return it. resolveCname(host, callback); } } exports.performGSSAPICanonicalizeHostName = performGSSAPICanonicalizeHostName; function resolveCname(host, callback) { // Attempt to resolve the host name dns.resolveCname(host, (err, r) => { if (err) return callback(undefined, host); // Get the first resolve host id if (r.length > 0) { return callback(undefined, r[0]); } callback(undefined, host); }); } exports.resolveCname = resolveCname; //# sourceMappingURL=gssapi.js.map