1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl
32 
33 #include "mongo/platform/basic.h"
34 
35 #include "mongo/db/auth/authorization_manager.h"
36 
37 #include <memory>
38 #include <string>
39 #include <vector>
40 
41 #include "mongo/base/init.h"
42 #include "mongo/base/status.h"
43 #include "mongo/bson/mutable/document.h"
44 #include "mongo/bson/mutable/element.h"
45 #include "mongo/bson/util/bson_extract.h"
46 #include "mongo/crypto/mechanism_scram.h"
47 #include "mongo/db/auth/action_set.h"
48 #include "mongo/db/auth/address_restriction.h"
49 #include "mongo/db/auth/authorization_session.h"
50 #include "mongo/db/auth/authz_manager_external_state.h"
51 #include "mongo/db/auth/privilege.h"
52 #include "mongo/db/auth/privilege_parser.h"
53 #include "mongo/db/auth/role_graph.h"
54 #include "mongo/db/auth/sasl_options.h"
55 #include "mongo/db/auth/user.h"
56 #include "mongo/db/auth/user_document_parser.h"
57 #include "mongo/db/auth/user_name.h"
58 #include "mongo/db/auth/user_name_hash.h"
59 #include "mongo/db/jsobj.h"
60 #include "mongo/db/mongod_options.h"
61 #include "mongo/platform/compiler.h"
62 #include "mongo/platform/unordered_map.h"
63 #include "mongo/stdx/memory.h"
64 #include "mongo/stdx/mutex.h"
65 #include "mongo/util/assert_util.h"
66 #include "mongo/util/log.h"
67 #include "mongo/util/mongoutils/str.h"
68 
69 namespace mongo {
70 
71 using std::begin;
72 using std::end;
73 using std::endl;
74 using std::back_inserter;
75 using std::string;
76 using std::vector;
77 
78 AuthInfo internalSecurity;
79 
80 MONGO_INITIALIZER_WITH_PREREQUISITES(SetupInternalSecurityUser, ("EndStartupOptionStorage"))
81 (InitializerContext* const context) try {
82     User* user = new User(UserName("__system", "local"));
83 
84     user->incrementRefCount();  // Pin this user so the ref count never drops below 1.
85     ActionSet allActions;
86     allActions.addAllActions();
87     PrivilegeVector privileges;
88     RoleGraph::generateUniversalPrivileges(&privileges);
89     user->addPrivileges(privileges);
90 
91     if (mongodGlobalParams.whitelistedClusterNetwork) {
92         const auto& whitelist = *mongodGlobalParams.whitelistedClusterNetwork;
93 
94         auto restriction = stdx::make_unique<ClientSourceRestriction>(whitelist);
95         auto restrictionSet = stdx::make_unique<RestrictionSet<>>(std::move(restriction));
96         auto restrictionDocument =
97             stdx::make_unique<RestrictionDocument<>>(std::move(restrictionSet));
98 
99         RestrictionDocuments clusterWhiteList(std::move(restrictionDocument));
100 
101         user->setRestrictions(std::move(clusterWhiteList));
102     }
103 
104 
105     internalSecurity.user = user;
106 
107     return Status::OK();
108 } catch (...) {
109     return exceptionToStatus();
110 }
111 
112 const std::string AuthorizationManager::USERID_FIELD_NAME = "userId";
113 const std::string AuthorizationManager::USER_NAME_FIELD_NAME = "user";
114 const std::string AuthorizationManager::USER_DB_FIELD_NAME = "db";
115 const std::string AuthorizationManager::ROLE_NAME_FIELD_NAME = "role";
116 const std::string AuthorizationManager::ROLE_DB_FIELD_NAME = "db";
117 const std::string AuthorizationManager::PASSWORD_FIELD_NAME = "pwd";
118 const std::string AuthorizationManager::V1_USER_NAME_FIELD_NAME = "user";
119 const std::string AuthorizationManager::V1_USER_SOURCE_FIELD_NAME = "userSource";
120 
121 const NamespaceString AuthorizationManager::adminCommandNamespace("admin.$cmd");
122 const NamespaceString AuthorizationManager::rolesCollectionNamespace("admin.system.roles");
123 const NamespaceString AuthorizationManager::usersAltCollectionNamespace("admin.system.new_users");
124 const NamespaceString AuthorizationManager::usersBackupCollectionNamespace(
125     "admin.system.backup_users");
126 const NamespaceString AuthorizationManager::usersCollectionNamespace("admin.system.users");
127 const NamespaceString AuthorizationManager::versionCollectionNamespace("admin.system.version");
128 const NamespaceString AuthorizationManager::defaultTempUsersCollectionNamespace("admin.tempusers");
129 const NamespaceString AuthorizationManager::defaultTempRolesCollectionNamespace("admin.temproles");
130 
131 const Status AuthorizationManager::authenticationFailedStatus(ErrorCodes::AuthenticationFailed,
132                                                               "Authentication failed.");
133 
134 const BSONObj AuthorizationManager::versionDocumentQuery = BSON("_id"
135                                                                 << "authSchema");
136 
137 const std::string AuthorizationManager::schemaVersionFieldName = "currentVersion";
138 
139 const int AuthorizationManager::schemaVersion24;
140 const int AuthorizationManager::schemaVersion26Upgrade;
141 const int AuthorizationManager::schemaVersion26Final;
142 const int AuthorizationManager::schemaVersion28SCRAM;
143 
144 /**
145  * Guard object for synchronizing accesses to data cached in AuthorizationManager instances.
146  * This guard allows one thread to access the cache at a time, and provides an exception-safe
147  * mechanism for a thread to release the cache mutex while performing network or disk operations
148  * while allowing other readers to proceed.
149  *
150  * There are two ways to use this guard.  One may simply instantiate the guard like a
151  * std::lock_guard, and perform reads or writes of the cache.
152  *
153  * Alternatively, one may instantiate the guard, examine the cache, and then enter into an
154  * update mode by first wait()ing until otherUpdateInFetchPhase() is false, and then
155  * calling beginFetchPhase().  At this point, other threads may acquire the guard in the simple
156  * manner and do reads, but other threads may not enter into a fetch phase.  During the fetch
157  * phase, the thread should perform required network or disk activity to determine what update
158  * it will make to the cache.  Then, it should call endFetchPhase(), to reacquire the user cache
159  * mutex.  At that point, the thread can make its modifications to the cache and let the guard
160  * go out of scope.
161  *
162  * All updates by guards using a fetch-phase are totally ordered with respect to one another,
163  * and all guards using no fetch phase are totally ordered with respect to one another, but
164  * there is not a total ordering among all guard objects.
165  *
166  * The cached data has an associated counter, called the cache generation.  If the cache
167  * generation changes while a guard is in fetch phase, the fetched data should not be stored
168  * into the cache, because some invalidation event occurred during the fetch phase.
169  *
170  * NOTE: It is not safe to enter fetch phase while holding a database lock.  Fetch phase
171  * operations are allowed to acquire database locks themselves, so entering fetch while holding
172  * a database lock may lead to deadlock.
173  */
174 class AuthorizationManager::CacheGuard {
175     MONGO_DISALLOW_COPYING(CacheGuard);
176 
177 public:
178     enum FetchSynchronization { fetchSynchronizationAutomatic, fetchSynchronizationManual };
179 
180     /**
181      * Constructs a cache guard, locking the mutex that synchronizes user cache accesses.
182      */
CacheGuard(AuthorizationManager * authzManager,const FetchSynchronization sync=fetchSynchronizationAutomatic)183     CacheGuard(AuthorizationManager* authzManager,
184                const FetchSynchronization sync = fetchSynchronizationAutomatic)
185         : _isThisGuardInFetchPhase(false),
186           _authzManager(authzManager),
187           _lock(authzManager->_cacheMutex) {
188         if (fetchSynchronizationAutomatic == sync) {
189             synchronizeWithFetchPhase();
190         }
191     }
192 
193     /**
194      * Releases the mutex that synchronizes user cache access, if held, and notifies
195      * any threads waiting for their own opportunity to update the user cache.
196      */
~CacheGuard()197     ~CacheGuard() {
198         if (!_lock.owns_lock()) {
199             _lock.lock();
200         }
201         if (_isThisGuardInFetchPhase) {
202             fassert(17190, _authzManager->_isFetchPhaseBusy);
203             _authzManager->_isFetchPhaseBusy = false;
204             _authzManager->_fetchPhaseIsReady.notify_all();
205         }
206     }
207 
208     /**
209      * Returns true of the authzManager reports that it is in fetch phase.
210      */
otherUpdateInFetchPhase()211     bool otherUpdateInFetchPhase() {
212         return _authzManager->_isFetchPhaseBusy;
213     }
214 
215     /**
216      * Waits on the _authzManager->_fetchPhaseIsReady condition.
217      */
wait()218     void wait() {
219         fassert(17222, !_isThisGuardInFetchPhase);
220         _authzManager->_fetchPhaseIsReady.wait(_lock);
221     }
222 
223     /**
224      * Enters fetch phase, releasing the _authzManager->_cacheMutex after recording the current
225      * cache generation.
226      */
beginFetchPhase()227     void beginFetchPhase() {
228         fassert(17191, !_authzManager->_isFetchPhaseBusy);
229         _isThisGuardInFetchPhase = true;
230         _authzManager->_isFetchPhaseBusy = true;
231         _startGeneration = _authzManager->_cacheGeneration;
232         _lock.unlock();
233     }
234 
235     /**
236      * Exits the fetch phase, reacquiring the _authzManager->_cacheMutex.
237      */
endFetchPhase()238     void endFetchPhase() {
239         _lock.lock();
240         // We do not clear _authzManager->_isFetchPhaseBusy or notify waiters until
241         // ~CacheGuard(), for two reasons.  First, there's no value to notifying the waiters
242         // before you're ready to release the mutex, because they'll just go to sleep on the
243         // mutex.  Second, in order to meaningfully check the preconditions of
244         // isSameCacheGeneration(), we need a state that means "fetch phase was entered and now
245         // has been exited."  That state is _isThisGuardInFetchPhase == true and
246         // _lock.owns_lock() == true.
247     }
248 
249     /**
250      * Returns true if _authzManager->_cacheGeneration remained the same while this guard was
251      * in fetch phase.  Behavior is undefined if this guard never entered fetch phase.
252      *
253      * If this returns true, do not update the cached data with this
254      */
isSameCacheGeneration() const255     bool isSameCacheGeneration() const {
256         fassert(17223, _isThisGuardInFetchPhase);
257         fassert(17231, _lock.owns_lock());
258         return _startGeneration == _authzManager->_cacheGeneration;
259     }
260 
261 private:
synchronizeWithFetchPhase()262     void synchronizeWithFetchPhase() {
263         while (otherUpdateInFetchPhase())
264             wait();
265         fassert(17192, !_authzManager->_isFetchPhaseBusy);
266         _isThisGuardInFetchPhase = true;
267         _authzManager->_isFetchPhaseBusy = true;
268     }
269 
270     OID _startGeneration;
271     bool _isThisGuardInFetchPhase;
272     AuthorizationManager* _authzManager;
273     stdx::unique_lock<stdx::mutex> _lock;
274 };
275 
AuthorizationManager(std::unique_ptr<AuthzManagerExternalState> externalState)276 AuthorizationManager::AuthorizationManager(std::unique_ptr<AuthzManagerExternalState> externalState)
277     : _authEnabled(false),
278       _privilegeDocsExist(false),
279       _externalState(std::move(externalState)),
280       _version(schemaVersionInvalid),
281       _isFetchPhaseBusy(false) {
282     _updateCacheGeneration_inlock();
283 }
284 
~AuthorizationManager()285 AuthorizationManager::~AuthorizationManager() {
286     for (unordered_map<UserName, User*>::iterator it = _userCache.begin(); it != _userCache.end();
287          ++it) {
288         fassert(17265, it->second != internalSecurity.user);
289         delete it->second;
290     }
291 }
292 
makeAuthorizationSession()293 std::unique_ptr<AuthorizationSession> AuthorizationManager::makeAuthorizationSession() {
294     return stdx::make_unique<AuthorizationSession>(
295         _externalState->makeAuthzSessionExternalState(this));
296 }
297 
setShouldValidateAuthSchemaOnStartup(bool validate)298 void AuthorizationManager::setShouldValidateAuthSchemaOnStartup(bool validate) {
299     _startupAuthSchemaValidation = validate;
300 }
301 
shouldValidateAuthSchemaOnStartup()302 bool AuthorizationManager::shouldValidateAuthSchemaOnStartup() {
303     return _startupAuthSchemaValidation;
304 }
305 
getAuthorizationVersion(OperationContext * opCtx,int * version)306 Status AuthorizationManager::getAuthorizationVersion(OperationContext* opCtx, int* version) {
307     CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
308     int newVersion = _version;
309     if (schemaVersionInvalid == newVersion) {
310         while (guard.otherUpdateInFetchPhase())
311             guard.wait();
312         guard.beginFetchPhase();
313         Status status = _externalState->getStoredAuthorizationVersion(opCtx, &newVersion);
314         guard.endFetchPhase();
315         if (!status.isOK()) {
316             warning() << "Problem fetching the stored schema version of authorization data: "
317                       << redact(status);
318             *version = schemaVersionInvalid;
319             return status;
320         }
321 
322         if (guard.isSameCacheGeneration()) {
323             _version = newVersion;
324         }
325     }
326     *version = newVersion;
327     return Status::OK();
328 }
329 
getCacheGeneration()330 OID AuthorizationManager::getCacheGeneration() {
331     CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
332     return _cacheGeneration;
333 }
334 
setAuthEnabled(bool enabled)335 void AuthorizationManager::setAuthEnabled(bool enabled) {
336     _authEnabled = enabled;
337 }
338 
isAuthEnabled() const339 bool AuthorizationManager::isAuthEnabled() const {
340     return _authEnabled;
341 }
342 
hasAnyPrivilegeDocuments(OperationContext * opCtx)343 bool AuthorizationManager::hasAnyPrivilegeDocuments(OperationContext* opCtx) {
344     stdx::unique_lock<stdx::mutex> lk(_privilegeDocsExistMutex);
345     if (_privilegeDocsExist) {
346         // If we know that a user exists, don't re-check.
347         return true;
348     }
349 
350     lk.unlock();
351     bool privDocsExist = _externalState->hasAnyPrivilegeDocuments(opCtx);
352     lk.lock();
353 
354     if (privDocsExist) {
355         _privilegeDocsExist = true;
356     }
357 
358     return _privilegeDocsExist;
359 }
360 
getBSONForPrivileges(const PrivilegeVector & privileges,mutablebson::Element resultArray)361 Status AuthorizationManager::getBSONForPrivileges(const PrivilegeVector& privileges,
362                                                   mutablebson::Element resultArray) {
363     for (PrivilegeVector::const_iterator it = privileges.begin(); it != privileges.end(); ++it) {
364         std::string errmsg;
365         ParsedPrivilege privilege;
366         if (!ParsedPrivilege::privilegeToParsedPrivilege(*it, &privilege, &errmsg)) {
367             return Status(ErrorCodes::BadValue, errmsg);
368         }
369         resultArray.appendObject("privileges", privilege.toBSON()).transitional_ignore();
370     }
371     return Status::OK();
372 }
373 
getBSONForRole(RoleGraph * graph,const RoleName & roleName,mutablebson::Element result)374 Status AuthorizationManager::getBSONForRole(RoleGraph* graph,
375                                             const RoleName& roleName,
376                                             mutablebson::Element result) try {
377     if (!graph->roleExists(roleName)) {
378         return Status(ErrorCodes::RoleNotFound,
379                       mongoutils::str::stream() << roleName.getFullName()
380                                                 << "does not name an existing role");
381     }
382     std::string id = mongoutils::str::stream() << roleName.getDB() << "." << roleName.getRole();
383     uassertStatusOK(result.appendString("_id", id));
384     uassertStatusOK(
385         result.appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME, roleName.getRole()));
386     uassertStatusOK(
387         result.appendString(AuthorizationManager::ROLE_DB_FIELD_NAME, roleName.getDB()));
388 
389     // Build privileges array
390     mutablebson::Element privilegesArrayElement =
391         result.getDocument().makeElementArray("privileges");
392     uassertStatusOK(result.pushBack(privilegesArrayElement));
393     const PrivilegeVector& privileges = graph->getDirectPrivileges(roleName);
394     uassertStatusOK(getBSONForPrivileges(privileges, privilegesArrayElement));
395 
396     // Build roles array
397     mutablebson::Element rolesArrayElement = result.getDocument().makeElementArray("roles");
398     uassertStatusOK(result.pushBack(rolesArrayElement));
399     for (RoleNameIterator roles = graph->getDirectSubordinates(roleName); roles.more();
400          roles.next()) {
401         const RoleName& subRole = roles.get();
402         mutablebson::Element roleObj = result.getDocument().makeElementObject("");
403         uassertStatusOK(
404             roleObj.appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME, subRole.getRole()));
405         uassertStatusOK(
406             roleObj.appendString(AuthorizationManager::ROLE_DB_FIELD_NAME, subRole.getDB()));
407         uassertStatusOK(rolesArrayElement.pushBack(roleObj));
408     }
409 
410     // Build authentication restrictions
411     auto restrictions = graph->getDirectAuthenticationRestrictions(roleName);
412     mutablebson::Element authenticationRestrictionsElement =
413         result.getDocument().makeElementArray("authenticationRestrictions");
414     uassertStatusOK(result.pushBack(authenticationRestrictionsElement));
415     if (restrictions) {
416         uassertStatusOK(authenticationRestrictionsElement.setValueArray(restrictions->toBSON()));
417     }
418 
419     return Status::OK();
420 } catch (...) {
421     return exceptionToStatus();
422 }
423 
424 
_initializeUserFromPrivilegeDocument(User * user,const BSONObj & privDoc)425 Status AuthorizationManager::_initializeUserFromPrivilegeDocument(User* user,
426                                                                   const BSONObj& privDoc) {
427     V2UserDocumentParser parser;
428     std::string userName = parser.extractUserNameFromUserDocument(privDoc);
429     if (userName != user->getName().getUser()) {
430         return Status(ErrorCodes::BadValue,
431                       mongoutils::str::stream() << "User name from privilege document \""
432                                                 << userName
433                                                 << "\" doesn't match name of provided User \""
434                                                 << user->getName().getUser()
435                                                 << "\"");
436     }
437 
438     user->setID(parser.extractUserIDFromUserDocument(privDoc));
439 
440     Status status = parser.initializeUserCredentialsFromUserDocument(user, privDoc);
441     if (!status.isOK()) {
442         return status;
443     }
444     status = parser.initializeUserRolesFromUserDocument(privDoc, user);
445     if (!status.isOK()) {
446         return status;
447     }
448     status = parser.initializeUserIndirectRolesFromUserDocument(privDoc, user);
449     if (!status.isOK()) {
450         return status;
451     }
452     status = parser.initializeUserPrivilegesFromUserDocument(privDoc, user);
453     if (!status.isOK()) {
454         return status;
455     }
456     status = parser.initializeAuthenticationRestrictionsFromUserDocument(privDoc, user);
457     if (!status.isOK()) {
458         return status;
459     }
460 
461     return Status::OK();
462 }
463 
getUserDescription(OperationContext * opCtx,const UserName & userName,BSONObj * result)464 Status AuthorizationManager::getUserDescription(OperationContext* opCtx,
465                                                 const UserName& userName,
466                                                 BSONObj* result) {
467     return _externalState->getUserDescription(opCtx, userName, result);
468 }
469 
getRoleDescription(OperationContext * opCtx,const RoleName & roleName,PrivilegeFormat privileges,AuthenticationRestrictionsFormat restrictions,BSONObj * result)470 Status AuthorizationManager::getRoleDescription(OperationContext* opCtx,
471                                                 const RoleName& roleName,
472                                                 PrivilegeFormat privileges,
473                                                 AuthenticationRestrictionsFormat restrictions,
474                                                 BSONObj* result) {
475     return _externalState->getRoleDescription(opCtx, roleName, privileges, restrictions, result);
476 }
477 
getRolesDescription(OperationContext * opCtx,const std::vector<RoleName> & roleName,PrivilegeFormat privileges,AuthenticationRestrictionsFormat restrictions,BSONObj * result)478 Status AuthorizationManager::getRolesDescription(OperationContext* opCtx,
479                                                  const std::vector<RoleName>& roleName,
480                                                  PrivilegeFormat privileges,
481                                                  AuthenticationRestrictionsFormat restrictions,
482                                                  BSONObj* result) {
483     return _externalState->getRolesDescription(opCtx, roleName, privileges, restrictions, result);
484 }
485 
486 
getRoleDescriptionsForDB(OperationContext * opCtx,const std::string dbname,PrivilegeFormat privileges,AuthenticationRestrictionsFormat restrictions,bool showBuiltinRoles,vector<BSONObj> * result)487 Status AuthorizationManager::getRoleDescriptionsForDB(OperationContext* opCtx,
488                                                       const std::string dbname,
489                                                       PrivilegeFormat privileges,
490                                                       AuthenticationRestrictionsFormat restrictions,
491                                                       bool showBuiltinRoles,
492                                                       vector<BSONObj>* result) {
493     return _externalState->getRoleDescriptionsForDB(
494         opCtx, dbname, privileges, restrictions, showBuiltinRoles, result);
495 }
496 
acquireUser(OperationContext * opCtx,const UserName & userName,User ** acquiredUser)497 Status AuthorizationManager::acquireUser(OperationContext* opCtx,
498                                          const UserName& userName,
499                                          User** acquiredUser) {
500     if (userName == internalSecurity.user->getName()) {
501         *acquiredUser = internalSecurity.user;
502         return Status::OK();
503     }
504 
505     unordered_map<UserName, User*>::iterator it;
506 
507     CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
508     while ((_userCache.end() == (it = _userCache.find(userName))) &&
509            guard.otherUpdateInFetchPhase()) {
510         guard.wait();
511     }
512 
513     if (it != _userCache.end()) {
514         fassert(16914, it->second);
515         fassert(17003, it->second->isValid());
516         fassert(17008, it->second->getRefCount() > 0);
517         it->second->incrementRefCount();
518         *acquiredUser = it->second;
519         return Status::OK();
520     }
521 
522     std::unique_ptr<User> user;
523 
524     int authzVersion = _version;
525     guard.beginFetchPhase();
526 
527     // Number of times to retry a user document that fetches due to transient
528     // AuthSchemaIncompatible errors.  These errors should only ever occur during and shortly
529     // after schema upgrades.
530     static const int maxAcquireRetries = 2;
531     Status status = Status::OK();
532     for (int i = 0; i < maxAcquireRetries; ++i) {
533         if (authzVersion == schemaVersionInvalid) {
534             Status status = _externalState->getStoredAuthorizationVersion(opCtx, &authzVersion);
535             if (!status.isOK())
536                 return status;
537         }
538 
539         switch (authzVersion) {
540             default:
541                 status = Status(ErrorCodes::BadValue,
542                                 mongoutils::str::stream()
543                                     << "Illegal value for authorization data schema version, "
544                                     << authzVersion);
545                 break;
546             case schemaVersion28SCRAM:
547             case schemaVersion26Final:
548             case schemaVersion26Upgrade:
549                 status = _fetchUserV2(opCtx, userName, &user);
550                 break;
551             case schemaVersion24:
552                 status = Status(ErrorCodes::AuthSchemaIncompatible,
553                                 mongoutils::str::stream()
554                                     << "Authorization data schema version "
555                                     << schemaVersion24
556                                     << " not supported after MongoDB version 2.6.");
557                 break;
558         }
559         if (status.isOK())
560             break;
561         if (status != ErrorCodes::AuthSchemaIncompatible)
562             return status;
563 
564         authzVersion = schemaVersionInvalid;
565     }
566     if (!status.isOK())
567         return status;
568 
569     guard.endFetchPhase();
570 
571     user->incrementRefCount();
572     // NOTE: It is not safe to throw an exception from here to the end of the method.
573     if (guard.isSameCacheGeneration()) {
574         _userCache.insert(std::make_pair(userName, user.get()));
575         if (_version == schemaVersionInvalid)
576             _version = authzVersion;
577     } else {
578         // If the cache generation changed while this thread was in fetch mode, the data
579         // associated with the user may now be invalid, so we must mark it as such.  The caller
580         // may still opt to use the information for a short while, but not indefinitely.
581         user->invalidate();
582     }
583     *acquiredUser = user.release();
584 
585     return Status::OK();
586 }
587 
acquireUserForSessionRefresh(OperationContext * opCtx,const UserName & userName,const User::UserId & uid,User ** user)588 Status AuthorizationManager::acquireUserForSessionRefresh(OperationContext* opCtx,
589                                                           const UserName& userName,
590                                                           const User::UserId& uid,
591                                                           User** user) {
592     auto status = acquireUser(opCtx, userName, user);
593     if (!status.isOK()) {
594         return status;
595     }
596 
597     if (uid != (*user)->getID()) {
598         releaseUser(*user);
599         *user = nullptr;
600         return {ErrorCodes::UserNotFound,
601                 str::stream() << "User id from privilege document '" << userName.toString()
602                               << "' does not match user id in session."};
603     }
604 
605     return Status::OK();
606 }
607 
_fetchUserV2(OperationContext * opCtx,const UserName & userName,std::unique_ptr<User> * acquiredUser)608 Status AuthorizationManager::_fetchUserV2(OperationContext* opCtx,
609                                           const UserName& userName,
610                                           std::unique_ptr<User>* acquiredUser) {
611     BSONObj userObj;
612     Status status = getUserDescription(opCtx, userName, &userObj);
613     if (!status.isOK()) {
614         return status;
615     }
616 
617     // Put the new user into an unique_ptr temporarily in case there's an error while
618     // initializing the user.
619     auto user = stdx::make_unique<User>(userName);
620 
621     status = _initializeUserFromPrivilegeDocument(user.get(), userObj);
622     if (!status.isOK()) {
623         return status;
624     }
625     acquiredUser->reset(user.release());
626     return Status::OK();
627 }
628 
releaseUser(User * user)629 void AuthorizationManager::releaseUser(User* user) {
630     if (user == internalSecurity.user) {
631         return;
632     }
633 
634     CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
635     user->decrementRefCount();
636     if (user->getRefCount() == 0) {
637         // If it's been invalidated then it's not in the _userCache anymore.
638         if (user->isValid()) {
639             MONGO_COMPILER_VARIABLE_UNUSED bool erased = _userCache.erase(user->getName());
640             dassert(erased);
641         }
642         delete user;
643     }
644 }
645 
invalidateUserByName(const UserName & userName)646 void AuthorizationManager::invalidateUserByName(const UserName& userName) {
647     CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
648     _updateCacheGeneration_inlock();
649     unordered_map<UserName, User*>::iterator it = _userCache.find(userName);
650     if (it == _userCache.end()) {
651         return;
652     }
653 
654     User* user = it->second;
655     _userCache.erase(it);
656     user->invalidate();
657 }
658 
invalidateUsersFromDB(const std::string & dbname)659 void AuthorizationManager::invalidateUsersFromDB(const std::string& dbname) {
660     CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
661     _updateCacheGeneration_inlock();
662     unordered_map<UserName, User*>::iterator it = _userCache.begin();
663     while (it != _userCache.end()) {
664         User* user = it->second;
665         if (user->getName().getDB() == dbname) {
666             _userCache.erase(it++);
667             user->invalidate();
668         } else {
669             ++it;
670         }
671     }
672 }
673 
invalidateUserCache()674 void AuthorizationManager::invalidateUserCache() {
675     CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
676     _invalidateUserCache_inlock();
677 }
678 
_invalidateUserCache_inlock()679 void AuthorizationManager::_invalidateUserCache_inlock() {
680     _updateCacheGeneration_inlock();
681     for (unordered_map<UserName, User*>::iterator it = _userCache.begin(); it != _userCache.end();
682          ++it) {
683         fassert(17266, it->second != internalSecurity.user);
684         it->second->invalidate();
685     }
686     _userCache.clear();
687 
688     // Reread the schema version before acquiring the next user.
689     _version = schemaVersionInvalid;
690 }
691 
initialize(OperationContext * opCtx)692 Status AuthorizationManager::initialize(OperationContext* opCtx) {
693     invalidateUserCache();
694     Status status = _externalState->initialize(opCtx);
695     if (!status.isOK())
696         return status;
697 
698     return Status::OK();
699 }
700 
701 namespace {
isAuthzNamespace(const NamespaceString & nss)702 bool isAuthzNamespace(const NamespaceString& nss) {
703     return (nss == AuthorizationManager::rolesCollectionNamespace ||
704             nss == AuthorizationManager::usersCollectionNamespace ||
705             nss == AuthorizationManager::versionCollectionNamespace);
706 }
707 
isAuthzCollection(StringData coll)708 bool isAuthzCollection(StringData coll) {
709     return (coll == AuthorizationManager::rolesCollectionNamespace.coll() ||
710             coll == AuthorizationManager::usersCollectionNamespace.coll() ||
711             coll == AuthorizationManager::versionCollectionNamespace.coll());
712 }
713 
loggedCommandOperatesOnAuthzData(const NamespaceString & nss,const BSONObj & cmdObj)714 bool loggedCommandOperatesOnAuthzData(const NamespaceString& nss, const BSONObj& cmdObj) {
715     if (nss != AuthorizationManager::adminCommandNamespace)
716         return false;
717     const StringData cmdName(cmdObj.firstElement().fieldNameStringData());
718     if (cmdName == "drop") {
719         return isAuthzCollection(cmdObj.firstElement().valueStringData());
720     } else if (cmdName == "dropDatabase") {
721         return true;
722     } else if (cmdName == "renameCollection") {
723         const NamespaceString fromNamespace(cmdObj.firstElement().str());
724         const NamespaceString toNamespace(cmdObj["to"].str());
725         if (fromNamespace.db() == "admin" || toNamespace.db() == "admin") {
726             return isAuthzCollection(fromNamespace.coll().toString()) ||
727                 isAuthzCollection(toNamespace.coll().toString());
728         } else {
729             return false;
730         }
731     } else if (cmdName == "dropIndexes" || cmdName == "deleteIndexes") {
732         return false;
733     } else if (cmdName == "create") {
734         return false;
735     } else {
736         return true;
737     }
738 }
739 
appliesToAuthzData(const char * op,const NamespaceString & nss,const BSONObj & o)740 bool appliesToAuthzData(const char* op, const NamespaceString& nss, const BSONObj& o) {
741     switch (*op) {
742         case 'i':
743         case 'u':
744         case 'd':
745             if (op[1] != '\0')
746                 return false;  // "db" op type
747             return isAuthzNamespace(nss);
748         case 'c':
749             return loggedCommandOperatesOnAuthzData(nss, o);
750             break;
751         case 'n':
752             return false;
753         default:
754             return true;
755     }
756 }
757 
758 // Updates to users in the oplog are done by matching on the _id, which will always have the
759 // form "<dbname>.<username>".  This function extracts the UserName from that string.
extractUserNameFromIdString(StringData idstr)760 StatusWith<UserName> extractUserNameFromIdString(StringData idstr) {
761     size_t splitPoint = idstr.find('.');
762     if (splitPoint == string::npos) {
763         return StatusWith<UserName>(ErrorCodes::FailedToParse,
764                                     mongoutils::str::stream()
765                                         << "_id entries for user documents must be of "
766                                            "the form <dbname>.<username>.  Found: "
767                                         << idstr);
768     }
769     return StatusWith<UserName>(
770         UserName(idstr.substr(splitPoint + 1), idstr.substr(0, splitPoint)));
771 }
772 
773 }  // namespace
774 
_updateCacheGeneration_inlock()775 void AuthorizationManager::_updateCacheGeneration_inlock() {
776     _cacheGeneration = OID::gen();
777 }
778 
_invalidateRelevantCacheData(const char * op,const NamespaceString & ns,const BSONObj & o,const BSONObj * o2)779 void AuthorizationManager::_invalidateRelevantCacheData(const char* op,
780                                                         const NamespaceString& ns,
781                                                         const BSONObj& o,
782                                                         const BSONObj* o2) {
783     if (ns == AuthorizationManager::rolesCollectionNamespace ||
784         ns == AuthorizationManager::versionCollectionNamespace) {
785         invalidateUserCache();
786         return;
787     }
788 
789     if (*op == 'i' || *op == 'd' || *op == 'u') {
790         // If you got into this function isAuthzNamespace() must have returned true, and we've
791         // already checked that it's not the roles or version collection.
792         invariant(ns == AuthorizationManager::usersCollectionNamespace);
793 
794         StatusWith<UserName> userName = (*op == 'u')
795             ? extractUserNameFromIdString((*o2)["_id"].str())
796             : extractUserNameFromIdString(o["_id"].str());
797 
798         if (!userName.isOK()) {
799             warning() << "Invalidating user cache based on user being updated failed, will "
800                          "invalidate the entire cache instead: "
801                       << userName.getStatus();
802             invalidateUserCache();
803             return;
804         }
805         invalidateUserByName(userName.getValue());
806     } else {
807         invalidateUserCache();
808     }
809 }
810 
logOp(OperationContext * opCtx,const char * op,const NamespaceString & nss,const BSONObj & o,const BSONObj * o2)811 void AuthorizationManager::logOp(OperationContext* opCtx,
812                                  const char* op,
813                                  const NamespaceString& nss,
814                                  const BSONObj& o,
815                                  const BSONObj* o2) {
816     if (appliesToAuthzData(op, nss, o)) {
817         _externalState->logOp(opCtx, op, nss, o, o2);
818         _invalidateRelevantCacheData(op, nss, o, o2);
819     }
820 }
821 
822 }  // namespace mongo
823