You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							188 lines
						
					
					
						
							7.4 KiB
						
					
					
				
			
		
		
	
	
							188 lines
						
					
					
						
							7.4 KiB
						
					
					
				"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) {
 | 
						|
    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 = mechanismProperties.SERVICE_NAME ?? 'mongodb';
 | 
						|
    performGSSAPICanonicalizeHostName(hostAddress.host, mechanismProperties, (err, host) => {
 | 
						|
        if (err)
 | 
						|
            return callback(err);
 | 
						|
        const initOptions = {};
 | 
						|
        if (password != null) {
 | 
						|
            Object.assign(initOptions, { user: username, password: password });
 | 
						|
        }
 | 
						|
        const spnHost = mechanismProperties.SERVICE_HOST ?? 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
 |