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