| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 | "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'});async function externalCommand(connection, command) {    return connection.commandAsync((0, utils_1.ns)('$external.$cmd'), command, undefined);}let krb;class GSSAPI extends auth_provider_1.AuthProvider {    async auth(authContext) {        const { connection, credentials } = authContext;        if (credentials == null) {            throw new error_1.MongoMissingCredentialsError('Credentials required for GSSAPI authentication');        }        const { username } = credentials;        const client = await makeKerberosClient(authContext);        const payload = await client.step('');        const saslStartResponse = await externalCommand(connection, saslStart(payload));        const negotiatedPayload = await negotiate(client, 10, saslStartResponse.payload);        const saslContinueResponse = await externalCommand(connection, saslContinue(negotiatedPayload, saslStartResponse.conversationId));        const finalizePayload = await finalize(client, username, saslContinueResponse.payload);        await externalCommand(connection, {            saslContinue: 1,            conversationId: saslContinueResponse.conversationId,            payload: finalizePayload        });    }}exports.GSSAPI = GSSAPI;async function makeKerberosClient(authContext) {    const { hostAddress } = authContext.options;    const { credentials } = authContext;    if (!hostAddress || typeof hostAddress.host !== 'string' || !credentials) {        throw new error_1.MongoInvalidArgumentError('Connection must have host and port and credentials defined.');    }    loadKrb();    if ('kModuleError' in krb) {        throw krb['kModuleError'];    }    const { initializeClient } = krb;    const { username, password } = credentials;    const mechanismProperties = credentials.mechanismProperties;    const serviceName = mechanismProperties.SERVICE_NAME ?? 'mongodb';    const host = await performGSSAPICanonicalizeHostName(hostAddress.host, mechanismProperties);    const initOptions = {};    if (password != null) {        // TODO(NODE-5139): These do not match the typescript options in initializeClient        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}`;    }    return initializeClient(spn, initOptions);}function saslStart(payload) {    return {        saslStart: 1,        mechanism: 'GSSAPI',        payload,        autoAuthorize: 1    };}function saslContinue(payload, conversationId) {    return {        saslContinue: 1,        conversationId,        payload    };}async function negotiate(client, retries, payload) {    try {        const response = await client.step(payload);        return response || '';    }    catch (error) {        if (retries === 0) {            // Retries exhausted, raise error            throw error;        }        // Adjust number of retries and call step again        return negotiate(client, retries - 1, payload);    }}async function finalize(client, user, payload) {    // GSS Client Unwrap    const response = await client.unwrap(payload);    return client.wrap(response || '', { user });}async function performGSSAPICanonicalizeHostName(host, mechanismProperties) {    const mode = mechanismProperties.CANONICALIZE_HOST_NAME;    if (!mode || mode === exports.GSSAPICanonicalizationValue.none) {        return host;    }    // If forward and reverse or true    if (mode === exports.GSSAPICanonicalizationValue.on ||        mode === exports.GSSAPICanonicalizationValue.forwardAndReverse) {        // Perform the lookup of the ip address.        const { address } = await dns.promises.lookup(host);        try {            // Perform a reverse ptr lookup on the ip address.            const results = await dns.promises.resolvePtr(address);            // If the ptr did not error but had no results, return the host.            return results.length > 0 ? results[0] : host;        }        catch (error) {            // 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.            return resolveCname(host);        }    }    else {        // The case for forward is just to resolve the cname as dns.lookup()        // will not return it.        return resolveCname(host);    }}exports.performGSSAPICanonicalizeHostName = performGSSAPICanonicalizeHostName;async function resolveCname(host) {    // Attempt to resolve the host name    try {        const results = await dns.promises.resolveCname(host);        // Get the first resolved host id        return results.length > 0 ? results[0] : host;    }    catch {        return host;    }}exports.resolveCname = resolveCname;/** * Load the Kerberos library. */function loadKrb() {    if (!krb) {        krb = (0, deps_1.getKerberos)();    }}//# sourceMappingURL=gssapi.js.map
 |