gssapi.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. "use strict";
  2. var f = require('util').format
  3. , crypto = require('crypto')
  4. , MongoError = require('../error');
  5. var AuthSession = function(db, username, password, options) {
  6. this.db = db;
  7. this.username = username;
  8. this.password = password;
  9. this.options = options;
  10. }
  11. AuthSession.prototype.equal = function(session) {
  12. return session.db == this.db
  13. && session.username == this.username
  14. && session.password == this.password;
  15. }
  16. // Kerberos class
  17. var Kerberos = null;
  18. var MongoAuthProcess = null;
  19. // Try to grab the Kerberos class
  20. try {
  21. Kerberos = require('kerberos').Kerberos
  22. // Authentication process for Mongo
  23. MongoAuthProcess = require('kerberos').processes.MongoAuthProcess
  24. } catch(err) {}
  25. /**
  26. * Creates a new GSSAPI authentication mechanism
  27. * @class
  28. * @return {GSSAPI} A cursor instance
  29. */
  30. var GSSAPI = function() {
  31. this.authStore = [];
  32. }
  33. /**
  34. * Authenticate
  35. * @method
  36. * @param {{Server}|{ReplSet}|{Mongos}} server Topology the authentication method is being called on
  37. * @param {Pool} pool Connection pool for this topology
  38. * @param {string} db Name of the database
  39. * @param {string} username Username
  40. * @param {string} password Password
  41. * @param {authResultCallback} callback The callback to return the result from the authentication
  42. * @return {object}
  43. */
  44. GSSAPI.prototype.auth = function(server, pool, db, username, password, options, callback) {
  45. var self = this;
  46. // We don't have the Kerberos library
  47. if(Kerberos == null) return callback(new Error("Kerberos library is not installed"));
  48. var gssapiServiceName = options['gssapiServiceName'] || 'mongodb';
  49. // Get all the connections
  50. var connections = pool.getAll();
  51. // Total connections
  52. var count = connections.length;
  53. if(count == 0) return callback(null, null);
  54. // Valid connections
  55. var numberOfValidConnections = 0;
  56. var credentialsValid = false;
  57. var errorObject = null;
  58. // For each connection we need to authenticate
  59. while(connections.length > 0) {
  60. // Execute MongoCR
  61. var execute = function(connection) {
  62. // Start Auth process for a connection
  63. GSSAPIInitialize(db, username, password, db, gssapiServiceName, server, connection, function(err, r) {
  64. // Adjust count
  65. count = count - 1;
  66. // If we have an error
  67. if(err) {
  68. errorObject = err;
  69. } else if(r.result['$err']) {
  70. errorObject = r.result;
  71. } else if(r.result['errmsg']) {
  72. errorObject = r.result;
  73. } else {
  74. credentialsValid = true;
  75. numberOfValidConnections = numberOfValidConnections + 1;
  76. }
  77. // We have authenticated all connections
  78. if(count == 0 && numberOfValidConnections > 0) {
  79. // Store the auth details
  80. addAuthSession(self.authStore, new AuthSession(db, username, password, options));
  81. // Return correct authentication
  82. callback(null, true);
  83. } else if(count == 0) {
  84. if(errorObject == null) errorObject = new MongoError(f("failed to authenticate using mongocr"));
  85. callback(errorObject, false);
  86. }
  87. });
  88. }
  89. // Get the connection
  90. execute(connections.shift());
  91. }
  92. }
  93. //
  94. // Initialize step
  95. var GSSAPIInitialize = function(db, username, password, authdb, gssapiServiceName, server, connection, callback) {
  96. // Create authenticator
  97. var mongo_auth_process = new MongoAuthProcess(connection.host, connection.port, gssapiServiceName);
  98. // Perform initialization
  99. mongo_auth_process.init(username, password, function(err, context) {
  100. if(err) return callback(err, false);
  101. // Perform the first step
  102. mongo_auth_process.transition('', function(err, payload) {
  103. if(err) return callback(err, false);
  104. // Call the next db step
  105. MongoDBGSSAPIFirstStep(mongo_auth_process, payload, db, username, password, authdb, server, connection, callback);
  106. });
  107. });
  108. }
  109. //
  110. // Perform first step against mongodb
  111. var MongoDBGSSAPIFirstStep = function(mongo_auth_process, payload, db, username, password, authdb, server, connection, callback) {
  112. // Build the sasl start command
  113. var command = {
  114. saslStart: 1
  115. , mechanism: 'GSSAPI'
  116. , payload: payload
  117. , autoAuthorize: 1
  118. };
  119. // Execute first sasl step
  120. server.command("$external.$cmd"
  121. , command
  122. , { connection: connection }, function(err, r) {
  123. if(err) return callback(err, false);
  124. var doc = r.result;
  125. // Execute mongodb transition
  126. mongo_auth_process.transition(r.result.payload, function(err, payload) {
  127. if(err) return callback(err, false);
  128. // MongoDB API Second Step
  129. MongoDBGSSAPISecondStep(mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback);
  130. });
  131. });
  132. }
  133. //
  134. // Perform first step against mongodb
  135. var MongoDBGSSAPISecondStep = function(mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback) {
  136. // Build Authentication command to send to MongoDB
  137. var command = {
  138. saslContinue: 1
  139. , conversationId: doc.conversationId
  140. , payload: payload
  141. };
  142. // Execute the command
  143. server.command("$external.$cmd"
  144. , command
  145. , { connection: connection }, function(err, r) {
  146. if(err) return callback(err, false);
  147. var doc = r.result;
  148. // Call next transition for kerberos
  149. mongo_auth_process.transition(doc.payload, function(err, payload) {
  150. if(err) return callback(err, false);
  151. // Call the last and third step
  152. MongoDBGSSAPIThirdStep(mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback);
  153. });
  154. });
  155. }
  156. var MongoDBGSSAPIThirdStep = function(mongo_auth_process, payload, doc, db, username, password, authdb, server, connection, callback) {
  157. // Build final command
  158. var command = {
  159. saslContinue: 1
  160. , conversationId: doc.conversationId
  161. , payload: payload
  162. };
  163. // Execute the command
  164. server.command("$external.$cmd"
  165. , command
  166. , { connection: connection }, function(err, r) {
  167. if(err) return callback(err, false);
  168. mongo_auth_process.transition(null, function(err, payload) {
  169. if(err) return callback(err, null);
  170. callback(null, r);
  171. });
  172. });
  173. }
  174. // Add to store only if it does not exist
  175. var addAuthSession = function(authStore, session) {
  176. var found = false;
  177. for(var i = 0; i < authStore.length; i++) {
  178. if(authStore[i].equal(session)) {
  179. found = true;
  180. break;
  181. }
  182. }
  183. if(!found) authStore.push(session);
  184. }
  185. /**
  186. * Re authenticate pool
  187. * @method
  188. * @param {{Server}|{ReplSet}|{Mongos}} server Topology the authentication method is being called on
  189. * @param {Pool} pool Connection pool for this topology
  190. * @param {authResultCallback} callback The callback to return the result from the authentication
  191. * @return {object}
  192. */
  193. GSSAPI.prototype.reauthenticate = function(server, pool, callback) {
  194. var count = this.authStore.length;
  195. if(count == 0) return callback(null, null);
  196. // Iterate over all the auth details stored
  197. for(var i = 0; i < this.authStore.length; i++) {
  198. this.auth(server, pool, this.authStore[i].db, this.authStore[i].username, this.authStore[i].password, this.authStore[i].options, function(err, r) {
  199. count = count - 1;
  200. // Done re-authenticating
  201. if(count == 0) {
  202. callback(null, null);
  203. }
  204. });
  205. }
  206. }
  207. /**
  208. * This is a result from a authentication strategy
  209. *
  210. * @callback authResultCallback
  211. * @param {error} error An error object. Set to null if no error present
  212. * @param {boolean} result The result of the authentication process
  213. */
  214. module.exports = GSSAPI;