gssapi.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.resolveCname = exports.performGSSAPICanonicalizeHostName = exports.GSSAPI = exports.GSSAPICanonicalizationValue = void 0;
  4. const dns = require("dns");
  5. const deps_1 = require("../../deps");
  6. const error_1 = require("../../error");
  7. const utils_1 = require("../../utils");
  8. const auth_provider_1 = require("./auth_provider");
  9. /** @public */
  10. exports.GSSAPICanonicalizationValue = Object.freeze({
  11. on: true,
  12. off: false,
  13. none: 'none',
  14. forward: 'forward',
  15. forwardAndReverse: 'forwardAndReverse'
  16. });
  17. class GSSAPI extends auth_provider_1.AuthProvider {
  18. auth(authContext, callback) {
  19. const { connection, credentials } = authContext;
  20. if (credentials == null)
  21. return callback(new error_1.MongoMissingCredentialsError('Credentials required for GSSAPI authentication'));
  22. const { username } = credentials;
  23. function externalCommand(command, cb) {
  24. return connection.command((0, utils_1.ns)('$external.$cmd'), command, undefined, cb);
  25. }
  26. makeKerberosClient(authContext, (err, client) => {
  27. if (err)
  28. return callback(err);
  29. if (client == null)
  30. return callback(new error_1.MongoMissingDependencyError('GSSAPI client missing'));
  31. client.step('', (err, payload) => {
  32. if (err)
  33. return callback(err);
  34. externalCommand(saslStart(payload), (err, result) => {
  35. if (err)
  36. return callback(err);
  37. if (result == null)
  38. return callback();
  39. negotiate(client, 10, result.payload, (err, payload) => {
  40. if (err)
  41. return callback(err);
  42. externalCommand(saslContinue(payload, result.conversationId), (err, result) => {
  43. if (err)
  44. return callback(err);
  45. if (result == null)
  46. return callback();
  47. finalize(client, username, result.payload, (err, payload) => {
  48. if (err)
  49. return callback(err);
  50. externalCommand({
  51. saslContinue: 1,
  52. conversationId: result.conversationId,
  53. payload
  54. }, (err, result) => {
  55. if (err)
  56. return callback(err);
  57. callback(undefined, result);
  58. });
  59. });
  60. });
  61. });
  62. });
  63. });
  64. });
  65. }
  66. }
  67. exports.GSSAPI = GSSAPI;
  68. function makeKerberosClient(authContext, callback) {
  69. var _a;
  70. const { hostAddress } = authContext.options;
  71. const { credentials } = authContext;
  72. if (!hostAddress || typeof hostAddress.host !== 'string' || !credentials) {
  73. return callback(new error_1.MongoInvalidArgumentError('Connection must have host and port and credentials defined.'));
  74. }
  75. if ('kModuleError' in deps_1.Kerberos) {
  76. return callback(deps_1.Kerberos['kModuleError']);
  77. }
  78. const { initializeClient } = deps_1.Kerberos;
  79. const { username, password } = credentials;
  80. const mechanismProperties = credentials.mechanismProperties;
  81. const serviceName = (_a = mechanismProperties.SERVICE_NAME) !== null && _a !== void 0 ? _a : 'mongodb';
  82. performGSSAPICanonicalizeHostName(hostAddress.host, mechanismProperties, (err, host) => {
  83. var _a;
  84. if (err)
  85. return callback(err);
  86. const initOptions = {};
  87. if (password != null) {
  88. Object.assign(initOptions, { user: username, password: password });
  89. }
  90. const spnHost = (_a = mechanismProperties.SERVICE_HOST) !== null && _a !== void 0 ? _a : host;
  91. let spn = `${serviceName}${process.platform === 'win32' ? '/' : '@'}${spnHost}`;
  92. if ('SERVICE_REALM' in mechanismProperties) {
  93. spn = `${spn}@${mechanismProperties.SERVICE_REALM}`;
  94. }
  95. initializeClient(spn, initOptions, (err, client) => {
  96. // TODO(NODE-3483)
  97. if (err)
  98. return callback(new error_1.MongoRuntimeError(err));
  99. callback(undefined, client);
  100. });
  101. });
  102. }
  103. function saslStart(payload) {
  104. return {
  105. saslStart: 1,
  106. mechanism: 'GSSAPI',
  107. payload,
  108. autoAuthorize: 1
  109. };
  110. }
  111. function saslContinue(payload, conversationId) {
  112. return {
  113. saslContinue: 1,
  114. conversationId,
  115. payload
  116. };
  117. }
  118. function negotiate(client, retries, payload, callback) {
  119. client.step(payload, (err, response) => {
  120. // Retries exhausted, raise error
  121. if (err && retries === 0)
  122. return callback(err);
  123. // Adjust number of retries and call step again
  124. if (err)
  125. return negotiate(client, retries - 1, payload, callback);
  126. // Return the payload
  127. callback(undefined, response || '');
  128. });
  129. }
  130. function finalize(client, user, payload, callback) {
  131. // GSS Client Unwrap
  132. client.unwrap(payload, (err, response) => {
  133. if (err)
  134. return callback(err);
  135. // Wrap the response
  136. client.wrap(response || '', { user }, (err, wrapped) => {
  137. if (err)
  138. return callback(err);
  139. // Return the payload
  140. callback(undefined, wrapped);
  141. });
  142. });
  143. }
  144. function performGSSAPICanonicalizeHostName(host, mechanismProperties, callback) {
  145. const mode = mechanismProperties.CANONICALIZE_HOST_NAME;
  146. if (!mode || mode === exports.GSSAPICanonicalizationValue.none) {
  147. return callback(undefined, host);
  148. }
  149. // If forward and reverse or true
  150. if (mode === exports.GSSAPICanonicalizationValue.on ||
  151. mode === exports.GSSAPICanonicalizationValue.forwardAndReverse) {
  152. // Perform the lookup of the ip address.
  153. dns.lookup(host, (error, address) => {
  154. // No ip found, return the error.
  155. if (error)
  156. return callback(error);
  157. // Perform a reverse ptr lookup on the ip address.
  158. dns.resolvePtr(address, (err, results) => {
  159. // This can error as ptr records may not exist for all ips. In this case
  160. // fallback to a cname lookup as dns.lookup() does not return the
  161. // cname.
  162. if (err) {
  163. return resolveCname(host, callback);
  164. }
  165. // If the ptr did not error but had no results, return the host.
  166. callback(undefined, results.length > 0 ? results[0] : host);
  167. });
  168. });
  169. }
  170. else {
  171. // The case for forward is just to resolve the cname as dns.lookup()
  172. // will not return it.
  173. resolveCname(host, callback);
  174. }
  175. }
  176. exports.performGSSAPICanonicalizeHostName = performGSSAPICanonicalizeHostName;
  177. function resolveCname(host, callback) {
  178. // Attempt to resolve the host name
  179. dns.resolveCname(host, (err, r) => {
  180. if (err)
  181. return callback(undefined, host);
  182. // Get the first resolve host id
  183. if (r.length > 0) {
  184. return callback(undefined, r[0]);
  185. }
  186. callback(undefined, host);
  187. });
  188. }
  189. exports.resolveCname = resolveCname;
  190. //# sourceMappingURL=gssapi.js.map