Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | 408x 408x 408x 408x 408x 408x 240x 235x 408x 99x 99x 99x 99x 95x 80x 80x 80x 80x 80x 99x 99x 99x 99x 99x 4x 95x 95x 95x 95x 95x 95x 95x 95x 95x 95x 95x 95x 80x 80x 80x 80x 80x 80x 80x 408x 147x 147x 48x 99x 90x 82x 82x 44x 38x 9x 408x 63x 63x 49x 14x 99x 9x | import * as dns from 'dns'; import { getKerberos, type Kerberos, type KerberosClient } from '../../deps'; import { MongoInvalidArgumentError, MongoMissingCredentialsError } from '../../error'; import { ns } from '../../utils'; import type { Connection } from '../connection'; import { type AuthContext, AuthProvider } from './auth_provider'; /** @public */ export const GSSAPICanonicalizationValue = Object.freeze({ on: true, off: false, none: 'none', forward: 'forward', forwardAndReverse: 'forwardAndReverse' } as const); /** @public */ export type GSSAPICanonicalizationValue = (typeof GSSAPICanonicalizationValue)[keyof typeof GSSAPICanonicalizationValue]; type MechanismProperties = { CANONICALIZE_HOST_NAME?: GSSAPICanonicalizationValue; SERVICE_HOST?: string; SERVICE_NAME?: string; SERVICE_REALM?: string; }; async function externalCommand( connection: Connection, command: ReturnType<typeof saslStart> | ReturnType<typeof saslContinue> ): Promise<{ payload: string; conversationId: number }> { const response = await connection.command(ns('$external.$cmd'), command); return response as { payload: string; conversationId: number }; } let krb: Kerberos; export class GSSAPI extends AuthProvider { override async auth(authContext: AuthContext): Promise<void> { const { connection, credentials } = authContext; Iif (credentials == null) { throw new 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 }); } } async function makeKerberosClient(authContext: AuthContext): Promise<KerberosClient> { const { hostAddress } = authContext.options; const { credentials } = authContext; Iif (!hostAddress || typeof hostAddress.host !== 'string' || !credentials) { throw new 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 as MechanismProperties; const serviceName = mechanismProperties.SERVICE_NAME ?? 'mongodb'; const host = await performGSSAPICanonicalizeHostName(hostAddress.host, mechanismProperties); const initOptions = {}; Eif (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}`; Iif ('SERVICE_REALM' in mechanismProperties) { spn = `${spn}@${mechanismProperties.SERVICE_REALM}`; } return await initializeClient(spn, initOptions); } function saslStart(payload: string) { return { saslStart: 1, mechanism: 'GSSAPI', payload, autoAuthorize: 1 } as const; } function saslContinue(payload: string, conversationId: number) { return { saslContinue: 1, conversationId, payload } as const; } async function negotiate( client: KerberosClient, retries: number, payload: string ): Promise<string> { 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 await negotiate(client, retries - 1, payload); } } async function finalize(client: KerberosClient, user: string, payload: string): Promise<string> { // GSS Client Unwrap const response = await client.unwrap(payload); return await client.wrap(response || '', { user }); } export async function performGSSAPICanonicalizeHostName( host: string, mechanismProperties: MechanismProperties ): Promise<string> { const mode = mechanismProperties.CANONICALIZE_HOST_NAME; if (!mode || mode === GSSAPICanonicalizationValue.none) { return host; } // If forward and reverse or true if ( mode === GSSAPICanonicalizationValue.on || mode === 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 { // 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 await resolveCname(host); } } else { // The case for forward is just to resolve the cname as dns.lookup() // will not return it. return await resolveCname(host); } } export async function resolveCname(host: string): Promise<string> { // 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; } } /** * Load the Kerberos library. */ function loadKrb() { if (!krb) { krb = getKerberos(); } } |