diff options
author | Spencer T Brody <spencer@10gen.com> | 2012-11-27 15:44:13 -0500 |
---|---|---|
committer | Spencer T Brody <spencer@10gen.com> | 2012-12-07 11:38:54 -0500 |
commit | faf643fbbc9cf187c6b76d13ecd395d1eec04161 (patch) | |
tree | b6ab3d9911793d039e498b5bb71f329d501aab9b | |
parent | 90440532fca3ce59383ad7e36437191f4819b3fc (diff) | |
download | mongo-faf643fbbc9cf187c6b76d13ecd395d1eec04161.tar.gz |
Check authorization in the new AuthorizationManager SERVER-7572
-rw-r--r-- | jstests/replsets/auth1.js | 6 | ||||
-rw-r--r-- | src/mongo/client/parallel.h | 2 | ||||
-rw-r--r-- | src/mongo/db/auth/action_types.txt | 6 | ||||
-rw-r--r-- | src/mongo/db/auth/auth_external_state_d.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/auth/auth_external_state_d.h | 2 | ||||
-rw-r--r-- | src/mongo/db/auth/auth_external_state_server_common.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.cpp | 121 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.h | 26 | ||||
-rw-r--r-- | src/mongo/db/clientcursor.cpp | 43 | ||||
-rw-r--r-- | src/mongo/db/clientcursor.h | 9 | ||||
-rw-r--r-- | src/mongo/db/dbcommands.cpp | 16 | ||||
-rw-r--r-- | src/mongo/db/index.cpp | 16 | ||||
-rw-r--r-- | src/mongo/db/instance.cpp | 36 | ||||
-rw-r--r-- | src/mongo/db/repl/heartbeat.cpp | 3 | ||||
-rw-r--r-- | src/mongo/s/commands_public.cpp | 13 | ||||
-rw-r--r-- | src/mongo/s/cursors.cpp | 9 | ||||
-rw-r--r-- | src/mongo/s/cursors.h | 2 | ||||
-rw-r--r-- | src/mongo/s/strategy.cpp | 19 | ||||
-rw-r--r-- | src/mongo/s/strategy_shard.cpp | 43 | ||||
-rw-r--r-- | src/mongo/s/strategy_single.cpp | 12 |
20 files changed, 348 insertions, 47 deletions
diff --git a/jstests/replsets/auth1.js b/jstests/replsets/auth1.js index eb12519f1eb..13b6b694f72 100644 --- a/jstests/replsets/auth1.js +++ b/jstests/replsets/auth1.js @@ -81,11 +81,11 @@ function doQueryOn(p) { if (typeof(JSON) != "undefined") { err = JSON.parse(e.substring(6)); } - else if (e.indexOf("10057") > 0) { - err.code = 10057; + else if (e.indexOf("16536") > 0) { + err.code = 16536; } } - assert.eq(err.code, 10057); + assert.eq(err.code, 16536); }; doQueryOn(slave); diff --git a/src/mongo/client/parallel.h b/src/mongo/client/parallel.h index 23f248c71d2..cb7fdad6eaf 100644 --- a/src/mongo/client/parallel.h +++ b/src/mongo/client/parallel.h @@ -79,6 +79,8 @@ namespace mongo { /** call before using */ void init(); + virtual std::string getNS() { return _ns; } + virtual bool more() = 0; virtual BSONObj next() = 0; diff --git a/src/mongo/db/auth/action_types.txt b/src/mongo/db/auth/action_types.txt index d48f5dbea6f..1b1ef965a45 100644 --- a/src/mongo/db/auth/action_types.txt +++ b/src/mongo/db/auth/action_types.txt @@ -37,7 +37,9 @@ "getShardVersion", "handshake", "hostInfo", +"inprog", "insert", +"killop", "listDatabases", "listShards", "logRotate", @@ -45,7 +47,8 @@ "moveChunk", "movePrimary", "netstat", -"profile", +"profileEnable", +"profileRead", "reIndex", "remove", "removeShard", @@ -73,6 +76,7 @@ "splitVector", "top", "touch", +"unlock", "unsetSharding", "update", "userAdmin", diff --git a/src/mongo/db/auth/auth_external_state_d.cpp b/src/mongo/db/auth/auth_external_state_d.cpp index ce15e4757ed..d6bfdfcb037 100644 --- a/src/mongo/db/auth/auth_external_state_d.cpp +++ b/src/mongo/db/auth/auth_external_state_d.cpp @@ -32,15 +32,21 @@ namespace mongo { const string& principalName, BSONObj* result) { Client::GodScope gs; + Client::ReadContext(dbname + ".system.users"); DBDirectClient conn; return getPrivilegeDocumentOverConnection(&conn, dbname, principalName, result); } bool AuthExternalStateMongod::hasPrivilegeDocument(const std::string& dbname) const { Client::GodScope gs; + Client::ReadContext(dbname + ".system.users"); DBDirectClient conn; BSONObj result = conn.findOne(dbname + ".system.users", Query()); return !result.isEmpty(); } + bool AuthExternalStateMongod::shouldIgnoreAuthChecks() const { + return cc().isGod() || AuthExternalStateServerCommon::shouldIgnoreAuthChecks(); + } + } // namespace mongo diff --git a/src/mongo/db/auth/auth_external_state_d.h b/src/mongo/db/auth/auth_external_state_d.h index 5a54744278a..ac0adcc0bad 100644 --- a/src/mongo/db/auth/auth_external_state_d.h +++ b/src/mongo/db/auth/auth_external_state_d.h @@ -36,6 +36,8 @@ namespace mongo { const string& principalName, BSONObj* result); + virtual bool shouldIgnoreAuthChecks() const; + protected: virtual bool hasPrivilegeDocument(const std::string& dbname) const; }; diff --git a/src/mongo/db/auth/auth_external_state_server_common.cpp b/src/mongo/db/auth/auth_external_state_server_common.cpp index 55e67f601d5..e5117b4ac7e 100644 --- a/src/mongo/db/auth/auth_external_state_server_common.cpp +++ b/src/mongo/db/auth/auth_external_state_server_common.cpp @@ -37,8 +37,9 @@ namespace mongo { } bool AuthExternalStateServerCommon::shouldIgnoreAuthChecks() const { - - return noauth || cc().isGod() || (cc().getIsLocalHostConnection() && _allowLocalhost()); + // TODO: cache if admin user exists and if it once existed don't query admin.system.users + ClientBasic* client = ClientBasic::getCurrent(); + return noauth || (client->getIsLocalHostConnection() && _allowLocalhost()); } } // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 77794801bb0..8b18e6a70f9 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -17,6 +17,7 @@ #include "mongo/db/auth/authorization_manager.h" #include <string> +#include <vector> #include "mongo/base/init.h" #include "mongo/base/status.h" @@ -98,6 +99,8 @@ namespace mongo { dbAdminRoleActions.addAction(ActionType::convertToCapped); dbAdminRoleActions.addAction(ActionType::dbStats); dbAdminRoleActions.addAction(ActionType::dropCollection); + dbAdminRoleActions.addAction(ActionType::profileEnable); + dbAdminRoleActions.addAction(ActionType::profileRead); dbAdminRoleActions.addAction(ActionType::reIndex); // TODO: Should readWrite have this also? This isn't consistent with ENSURE_INDEX and DROP_INDEXES dbAdminRoleActions.addAction(ActionType::renameCollection); dbAdminRoleActions.addAction(ActionType::validate); @@ -117,9 +120,10 @@ namespace mongo { serverAdminRoleActions.addAction(ActionType::getShardMap); serverAdminRoleActions.addAction(ActionType::getShardVersion); serverAdminRoleActions.addAction(ActionType::hostInfo); + serverAdminRoleActions.addAction(ActionType::inprog); + serverAdminRoleActions.addAction(ActionType::killop); serverAdminRoleActions.addAction(ActionType::listDatabases); serverAdminRoleActions.addAction(ActionType::logRotate); - serverAdminRoleActions.addAction(ActionType::profile); // TODO: should this be dbAdmin? serverAdminRoleActions.addAction(ActionType::repairDatabase); serverAdminRoleActions.addAction(ActionType::replSetFreeze); serverAdminRoleActions.addAction(ActionType::replSetGetStatus); @@ -133,6 +137,7 @@ namespace mongo { serverAdminRoleActions.addAction(ActionType::shutdown); serverAdminRoleActions.addAction(ActionType::top); serverAdminRoleActions.addAction(ActionType::touch); + serverAdminRoleActions.addAction(ActionType::unlock); // Cluster admin role clusterAdminRoleActions.addAction(ActionType::addShard); @@ -351,4 +356,118 @@ namespace mongo { return NULL; // Not authorized } + Status AuthorizationManager::checkAuthForQuery(const std::string& ns) { + NamespaceString namespaceString(ns); + verify(!namespaceString.isCommand()); + if (namespaceString.coll == "system.users") { + if (!checkAuthorization(ns, ActionType::userAdmin)) { + return Status(ErrorCodes::Unauthorized, + mongoutils::str::stream() << + "unauthorized to read user information for database " << + namespaceString.db, + 0); + } + } + else if (namespaceString.coll == "system.profile") { + if (!checkAuthorization(ns, ActionType::profileRead)) { + return Status(ErrorCodes::Unauthorized, + mongoutils::str::stream() << "unauthorized to read " << + namespaceString.db << ".system.profile", + 0); + } + } + else { + if (!checkAuthorization(ns, ActionType::find)) { + return Status(ErrorCodes::Unauthorized, + mongoutils::str::stream() << "unauthorized for query on " << ns, + 0); + } + } + return Status::OK(); + } + + Status AuthorizationManager::checkAuthForInsert(const std::string& ns) { + NamespaceString namespaceString(ns); + if (namespaceString.coll == "system.users") { + if (!checkAuthorization(ns, ActionType::userAdmin)) { + return Status(ErrorCodes::Unauthorized, + mongoutils::str::stream() << + "unauthorized to create user for database " << + namespaceString.db, + 0); + } + } + else { + if (!checkAuthorization(ns, ActionType::insert)) { + return Status(ErrorCodes::Unauthorized, + mongoutils::str::stream() << "unauthorized for insert on " << ns, + 0); + } + } + return Status::OK(); + } + + Status AuthorizationManager::checkAuthForUpdate(const std::string& ns, bool upsert) { + NamespaceString namespaceString(ns); + if (namespaceString.coll == "system.users") { + if (!checkAuthorization(ns, ActionType::userAdmin)) { + return Status(ErrorCodes::Unauthorized, + mongoutils::str::stream() << + "not authorized to update user information for database " << + namespaceString.db, + 0); + } + } + else { + if (!checkAuthorization(ns, ActionType::update)) { + return Status(ErrorCodes::Unauthorized, + mongoutils::str::stream() << "not authorized for update on " << ns, + 0); + } + if (upsert && !checkAuthorization(ns, ActionType::insert)) { + return Status(ErrorCodes::Unauthorized, + mongoutils::str::stream() << "not authorized for upsert on " << ns, + 0); + } + } + return Status::OK(); + } + + Status AuthorizationManager::checkAuthForDelete(const std::string& ns) { + NamespaceString namespaceString(ns); + if (namespaceString.coll == "system.users") { + if (!checkAuthorization(ns, ActionType::userAdmin)) { + return Status(ErrorCodes::Unauthorized, + mongoutils::str::stream() << + "not authorized to remove user from database " << + namespaceString.db, + 0); + } + } + else { + if (!checkAuthorization(ns, ActionType::remove)) { + return Status(ErrorCodes::Unauthorized, + mongoutils::str::stream() << "not authorized to remove from " << ns, + 0); + } + } + return Status::OK(); + } + + Status AuthorizationManager::checkAuthForGetMore(const std::string& ns) { + return checkAuthForQuery(ns); + } + + Status AuthorizationManager::checkAuthForPrivileges(const vector<Privilege>& privileges) { + for (std::vector<Privilege>::const_iterator it = privileges.begin(); + it != privileges.end(); ++it) { + const Privilege& privilege = *it; + ActionSet actions = privilege.getActions(); + if (!checkAuthorization(privilege.getResource(), privilege.getActions())) { + return Status(ErrorCodes::Unauthorized, "unauthorized", 0); + } + } + return Status::OK(); + } + } // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index 8eae9a1cf9c..865205f1b79 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -17,6 +17,7 @@ #pragma once #include <string> +#include <vector> #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" @@ -103,11 +104,34 @@ namespace mongo { return _externalState->getPrivilegeDocument(dbname, userName, result); } + // Checks if this connection has the privileges necessary to perform a query on the given + // namespace. + Status checkAuthForQuery(const std::string& ns); + + // Checks if this connection has the privileges necessary to perform an update on the given + // namespace. + Status checkAuthForUpdate(const std::string& ns, bool upsert); + + // Checks if this connection has the privileges necessary to perform an insert to the given + // namespace. + Status checkAuthForInsert(const std::string& ns); + + // Checks if this connection has the privileges necessary to perform a delete on the given + // namespace. + Status checkAuthForDelete(const std::string& ns); + + // Checks if this connection has the privileges necessary to perform a getMore on the given + // namespace. + Status checkAuthForGetMore(const std::string& ns); + + // Checks if this connection is authorized for all the given Privileges + Status checkAuthForPrivileges(const vector<Privilege>& privileges); + // Given a database name and a readOnly flag return an ActionSet describing all the actions // that an old-style user with those attributes should be given. static ActionSet getActionsForOldStyleUser(const std::string& dbname, bool readOnly); - // Parses the privilege document and returns a PrivilegeSet of all the Capabilities that + // Parses the privilege document and returns a PrivilegeSet of all the Privileges that // the privilege document grants. static Status buildPrivilegeSet(const std::string& dbname, Principal* principal, diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp index e1e9ee5705d..562985023ec 100644 --- a/src/mongo/db/clientcursor.cpp +++ b/src/mongo/db/clientcursor.cpp @@ -847,35 +847,60 @@ namespace mongo { } } - bool ClientCursor::erase( CursorId id ) { - recursive_scoped_lock lock( ccmutex ); - ClientCursor *cursor = find_inlock( id ); + bool ClientCursor::_erase_inlock(ClientCursor* cursor) { if ( ! cursor ) return false; - if ( ! cc().getAuthenticationInfo()->isAuthorizedReads( nsToDatabase( cursor->ns() ) ) ) - return false; - // Must not have an active ClientCursor::Pin. massert( 16089, - str::stream() << "Cannot kill active cursor " << id, + str::stream() << "Cannot kill active cursor " << cursor->cursorid(), cursor->_pinValue < 100 ); - + delete cursor; return true; } + bool ClientCursor::erase(CursorId id) { + recursive_scoped_lock lock(ccmutex); + ClientCursor* cursor = find_inlock(id); + return _erase_inlock(cursor); + } + + bool ClientCursor::eraseIfAuthorized(CursorId id) { + recursive_scoped_lock lock(ccmutex); + ClientCursor* cursor = find_inlock(id); + + if (!cc().getAuthorizationManager()->checkAuthorization(cursor->ns(), + ActionType::find) + || !cc().getAuthenticationInfo()->isAuthorizedReads(nsToDatabase(cursor->ns()))) { + return false; + } + + return _erase_inlock(cursor); + } + int ClientCursor::erase(int n, long long *ids) { int found = 0; for ( int i = 0; i < n; i++ ) { - if ( erase(ids[i]) ) + if ( erase(ids[i])) found++; if ( inShutdown() ) break; } return found; + } + int ClientCursor::eraseIfAuthorized(int n, long long *ids) { + int found = 0; + for ( int i = 0; i < n; i++ ) { + if ( eraseIfAuthorized(ids[i])) + found++; + + if ( inShutdown() ) + break; + } + return found; } ClientCursor::YieldLock::YieldLock( ptr<ClientCursor> cc ) diff --git a/src/mongo/db/clientcursor.h b/src/mongo/db/clientcursor.h index 4c1373d5956..c50bbd81a37 100644 --- a/src/mongo/db/clientcursor.h +++ b/src/mongo/db/clientcursor.h @@ -324,13 +324,19 @@ namespace mongo { /** * Deletes the cursor with the provided @param 'id' if one exists. * @throw if the cursor with the provided id is pinned. + * This does not do any auth checking and should be used only when erasing cursors as part + * of cleaning up internal operations. */ static bool erase(CursorId id); + // Same as erase but checks to make sure this thread has read permission on the cursor's + // namespace. This should be called when receiving killCursors from a client. + static bool eraseIfAuthorized(CursorId id); /** * @return number of cursors found */ - static int erase( int n , long long * ids ); + static int erase(int n, long long* ids); + static int eraseIfAuthorized(int n, long long* ids); void mayUpgradeStorage() { /* if ( !ids_.get() ) @@ -377,6 +383,7 @@ namespace mongo { CCByLoc& byLoc() { return _db->ccByLoc; } Record* _recordForYield( RecordNeeds need ); + static bool _erase_inlock(ClientCursor* cursor); private: diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index 8f962b27325..34bc10a57ef 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -484,9 +484,8 @@ namespace mongo { const BSONObj& cmdObj, std::vector<Privilege>* out) { ActionSet actions; - actions.addAction(ActionType::profile); - // TODO: should the resource here be the database instead of server? - out->push_back(Privilege(AuthorizationManager::SERVER_RESOURCE_NAME, actions)); + actions.addAction(ActionType::profileEnable); + out->push_back(Privilege(dbname, actions)); } CmdProfile() : Command("profile") {} bool run(const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { @@ -1931,6 +1930,17 @@ namespace mongo { return false; } + if (c->requiresAuth()) { + std::vector<Privilege> privileges; + c->addRequiredPrivileges(dbname, cmdObj, &privileges); + Status status = client.getAuthorizationManager()->checkAuthForPrivileges(privileges); + if (!status.isOK()) { + result.append("errmsg", status.reason()); + log() << "command denied: " << cmdObj.toString() << endl; + return false; + } + } + if ( cmdObj["help"].trueValue() ) { client.curop()->ensureStarted(); stringstream ss; diff --git a/src/mongo/db/index.cpp b/src/mongo/db/index.cpp index 1925d2e5773..95f4575cdf4 100644 --- a/src/mongo/db/index.cpp +++ b/src/mongo/db/index.cpp @@ -22,6 +22,8 @@ #include <boost/checked_delete.hpp> +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_manager.h" #include "mongo/db/background.h" #include "mongo/db/btree.h" #include "mongo/db/index_update.h" @@ -29,6 +31,7 @@ #include "mongo/db/ops/delete.h" #include "mongo/db/repl/rs.h" #include "mongo/util/scopeguard.h" +#include "mongo/util/mongoutils/str.h" namespace mongo { @@ -292,16 +295,21 @@ namespace mongo { BSONObj& fixedIndexObject) { sourceCollection = 0; - // logical name of the index. todo: get rid of the name, we don't need it! - const char *name = io.getStringField("name"); - uassert(12523, "no index name specified", *name); - // the collection for which we are building an index sourceNS = io.getStringField("ns"); uassert(10096, "invalid ns to index", sourceNS.find( '.' ) != string::npos); massert(10097, str::stream() << "bad table to index name on add index attempt current db: " << cc().database()->name << " source: " << sourceNS , cc().database()->name == nsToDatabase(sourceNS.c_str())); + uassert(16548, + mongoutils::str::stream() << "not authorized to create index on " << sourceNS, + cc().getAuthorizationManager()->checkAuthorization(sourceNS, + ActionType::ensureIndex)); + + // logical name of the index. todo: get rid of the name, we don't need it! + const char *name = io.getStringField("name"); + uassert(12523, "no index name specified", *name); + BSONObj key = io.getObjectField("key"); uassert(12524, "index key pattern too large", key.objsize() <= 2048); if( !validKeyPattern(key) ) { diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp index 0e84e57e019..176a380ff26 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -28,7 +28,10 @@ #endif #include "mongo/util/time_support.h" +#include "mongo/base/status.h" #include "mongo/bson/util/atomic_int.h" +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_manager.h" #include "mongo/db/background.h" #include "mongo/db/cmdline.h" #include "mongo/db/commands/fsync.h" @@ -43,6 +46,7 @@ #include "mongo/db/json.h" #include "mongo/db/kill_current_op.h" #include "mongo/db/lasterror.h" +#include "mongo/db/namespacestring.h" #include "mongo/db/ops/count.h" #include "mongo/db/ops/delete.h" #include "mongo/db/ops/query.h" @@ -55,6 +59,7 @@ #include "mongo/s/d_logic.h" #include "mongo/util/file_allocator.h" #include "mongo/util/goodies.h" +#include "mongo/util/mongoutils/str.h" namespace mongo { @@ -148,7 +153,8 @@ namespace mongo { void inProgCmd( Message &m, DbResponse &dbresponse ) { BSONObjBuilder b; - if( ! cc().isAdmin() ) { + if (!cc().isAdmin() || !cc().getAuthorizationManager()->checkAuthorization( + AuthorizationManager::SERVER_RESOURCE_NAME, ActionType::inprog)) { b.append("err", "unauthorized"); } else { @@ -188,7 +194,8 @@ namespace mongo { void killOp( Message &m, DbResponse &dbresponse ) { BSONObj obj; - if( ! cc().isAdmin() ) { + if (!cc().isAdmin() || !cc().getAuthorizationManager()->checkAuthorization( + AuthorizationManager::SERVER_RESOURCE_NAME, ActionType::killop)) { obj = fromjson("{\"err\":\"unauthorized\"}"); } /*else if( !dbMutexInfo.isLocked() ) @@ -213,7 +220,8 @@ namespace mongo { bool _unlockFsync(); void unlockFsync(const char *ns, Message& m, DbResponse &dbresponse) { BSONObj obj; - if ( ! cc().isAdmin() ) { // checks auth + if (!cc().isAdmin() || !cc().getAuthorizationManager()->checkAuthorization( + AuthorizationManager::SERVER_RESOURCE_NAME, ActionType::unlock)) { obj = fromjson("{\"err\":\"unauthorized\"}"); } else if (strncmp(ns, "admin.", 6) != 0 ) { @@ -244,6 +252,11 @@ namespace mongo { shared_ptr<AssertionException> ex; try { + if (!NamespaceString(d.getns()).isCommand()) { + // Auth checking for Commands happens later. + Status status = cc().getAuthorizationManager()->checkAuthForQuery(d.getns()); + uassert(16550, status.reason(), status.isOK()); + } dbresponse.exhaustNS = runQuery(m, q, op, *resp); verify( !resp->empty() ); } @@ -500,7 +513,7 @@ namespace mongo { verify( n < 30000 ); } - int found = ClientCursor::erase(n, (long long *) x); + int found = ClientCursor::eraseIfAuthorized(n, (long long *) x); if ( logLevel > 0 || found != n ) { LOG( found == n ? 1 : 0 ) << "killcursors: found " << found << " of " << n << endl; @@ -554,7 +567,10 @@ namespace mongo { bool upsert = flags & UpdateOption_Upsert; bool multi = flags & UpdateOption_Multi; bool broadcast = flags & UpdateOption_Broadcast; - + + Status status = cc().getAuthorizationManager()->checkAuthForUpdate(ns, upsert); + uassert(16538, status.reason(), status.isOK()); + op.debug().query = query; op.setQuery(query); @@ -586,6 +602,10 @@ namespace mongo { void receivedDelete(Message& m, CurOp& op) { DbMessage d(m); const char *ns = d.getns(); + + Status status = cc().getAuthorizationManager()->checkAuthForDelete(ns); + uassert(16542, status.reason(), status.isOK()); + op.debug().ns = ns; int flags = d.pullInt(); bool justOne = flags & RemoveOption_JustOne; @@ -655,6 +675,9 @@ namespace mongo { const NamespaceString nsString( ns ); uassert( 16258, str::stream() << "Invalid ns [" << ns << "]", nsString.isValid() ); + Status status = cc().getAuthorizationManager()->checkAuthForGetMore(ns); + uassert(16543, status.reason(), status.isOK()); + if (str::startsWith(ns, "local.oplog.")){ if (pass == 0) { mutex::scoped_lock lk(OpTime::m); @@ -784,6 +807,9 @@ namespace mongo { const char *ns = d.getns(); op.debug().ns = ns; + Status status = cc().getAuthorizationManager()->checkAuthForInsert(ns); + uassert(16544, status.reason(), status.isOK()); + if( !d.moreJSObjs() ) { // strange. should we complain? return; diff --git a/src/mongo/db/repl/heartbeat.cpp b/src/mongo/db/repl/heartbeat.cpp index 34663ff7cb3..589d51a9ba2 100644 --- a/src/mongo/db/repl/heartbeat.cpp +++ b/src/mongo/db/repl/heartbeat.cpp @@ -245,8 +245,7 @@ namespace mongo { if( ok ) { up(info, mem); } - else if (!info["errmsg"].eoo() && - info["errmsg"].str() == "need to login") { + else if (!info["errmsg"].eoo() && info["errmsg"].str() == "unauthorized") { authIssue(mem); } else { diff --git a/src/mongo/s/commands_public.cpp b/src/mongo/s/commands_public.cpp index 08604cfd1ff..53a8272fc12 100644 --- a/src/mongo/s/commands_public.cpp +++ b/src/mongo/s/commands_public.cpp @@ -263,8 +263,8 @@ namespace mongo { const BSONObj& cmdObj, std::vector<Privilege>* out) { ActionSet actions; - actions.addAction(ActionType::profile); - out->push_back(Privilege(AuthorizationManager::SERVER_RESOURCE_NAME, actions)); + actions.addAction(ActionType::profileEnable); + out->push_back(Privilege(dbname, actions)); } } profileCmd; @@ -1844,10 +1844,15 @@ namespace mongo { char cl[256]; nsToDatabase(ns, cl); - if( c->requiresAuth() && !ai->isAuthorizedForLock(cl, c->locktype())) { + std::vector<Privilege> privileges; + c->addRequiredPrivileges(cl, jsobj, &privileges); + if (c->requiresAuth() && + (!client->getAuthorizationManager()->checkAuthForPrivileges(privileges).isOK() + || !ai->isAuthorizedForLock(cl, c->locktype()))) { ok = false; errmsg = "unauthorized"; - anObjBuilder.append( "note" , str::stream() << "need to authorized on db: " << cl << " for command: " << e.fieldName() ); + anObjBuilder.append("note", str::stream() << "unauthorized for command: " << + e.fieldName() << " on database " << cl); } else if( c->adminOnly() && c->localHostOnlyIfNoAuth( jsobj ) && noauth && !ai->isLocalHost() ) { ok = false; diff --git a/src/mongo/s/cursors.cpp b/src/mongo/s/cursors.cpp index 77a495e2efc..acdbc1a5b67 100644 --- a/src/mongo/s/cursors.cpp +++ b/src/mongo/s/cursors.cpp @@ -276,6 +276,8 @@ namespace mongo { uassert( 13287 , "too many cursors to kill" , n < 30000 ); long long * cursors = (long long *)x; + AuthorizationManager* authManager = + ClientBasic::getCurrent()->getAuthorizationManager(); for ( int i=0; i<n; i++ ) { long long id = cursors[i]; LOG(_myLogLevel) << "CursorCache::gotKillCursors id: " << id << endl; @@ -291,7 +293,9 @@ namespace mongo { MapSharded::iterator i = _cursors.find( id ); if ( i != _cursors.end() ) { - _cursors.erase( i ); + if (authManager->checkAuthorization(i->second->getNS(), ActionType::find)) { + _cursors.erase( i ); + } continue; } @@ -302,6 +306,9 @@ namespace mongo { continue; } verify(refsNSIt != _refsNS.end()); + if (!authManager->checkAuthorization(refsNSIt->second, ActionType::find)) { + continue; + } server = refsIt->second; _refs.erase(refsIt); _refsNS.erase(refsNSIt); diff --git a/src/mongo/s/cursors.h b/src/mongo/s/cursors.h index 8666d2d5497..b42ea7538d7 100644 --- a/src/mongo/s/cursors.h +++ b/src/mongo/s/cursors.h @@ -67,6 +67,8 @@ namespace mongo { /** @return idle time in ms */ long long idleTime( long long now ); + std::string getNS() { return _cursor->getNS(); } + // The default initial buffer size for sending responses. static const int INIT_REPLY_BUFFER_SIZE; diff --git a/src/mongo/s/strategy.cpp b/src/mongo/s/strategy.cpp index 7ce25991e5f..6964b2c4fd2 100644 --- a/src/mongo/s/strategy.cpp +++ b/src/mongo/s/strategy.cpp @@ -18,15 +18,18 @@ #include "pch.h" -#include "../client/connpool.h" -#include "../db/commands.h" +#include "mongo/s/strategy.h" + +#include "mongo/client/connpool.h" +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/commands.h" +#include "mongo/s/grid.h" +#include "mongo/s/request.h" +#include "mongo/s/server.h" +#include "mongo/s/writeback_listener.h" +#include "mongo/util/mongoutils/str.h" -#include "grid.h" -#include "request.h" -#include "server.h" -#include "writeback_listener.h" - -#include "strategy.h" namespace mongo { diff --git a/src/mongo/s/strategy_shard.cpp b/src/mongo/s/strategy_shard.cpp index 09caa5c77f0..6d50fc669b2 100644 --- a/src/mongo/s/strategy_shard.cpp +++ b/src/mongo/s/strategy_shard.cpp @@ -18,17 +18,22 @@ #include "pch.h" +#include "mongo/base/status.h" #include "mongo/bson/util/builder.h" #include "mongo/client/connpool.h" #include "mongo/client/dbclientcursor.h" +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_manager.h" #include "mongo/db/commands.h" #include "mongo/db/index.h" +#include "mongo/db/namespacestring.h" #include "mongo/s/client_info.h" #include "mongo/s/chunk.h" #include "mongo/s/cursors.h" #include "mongo/s/grid.h" #include "mongo/s/request.h" #include "mongo/s/stats.h" +#include "mongo/util/mongoutils/str.h" // error codes 8010-8040 @@ -46,6 +51,11 @@ namespace mongo { QueryMessage q( r.d() ); + AuthorizationManager* authManager = + ClientBasic::getCurrent()->getAuthorizationManager(); + Status status = authManager->checkAuthForQuery(q.ns); + uassert(16549, status.reason(), status.isOK()); + r.checkAuth( Auth::READ ); LOG(3) << "shard query: " << q.ns << " " << q.query << endl; @@ -165,6 +175,13 @@ namespace mongo { virtual void getMore( Request& r ) { + const char *ns = r.getns(); + + AuthorizationManager* authManager = + ClientBasic::getCurrent()->getAuthorizationManager(); + Status status = authManager->checkAuthForGetMore(ns); + uassert(16539, status.reason(), status.isOK()); + // TODO: Handle stale config exceptions here from coll being dropped or sharded during op // for now has same semantics as legacy request ChunkManagerPtr info = r.getChunkManager(); @@ -175,8 +192,6 @@ namespace mongo { if( ! info ){ - const char *ns = r.getns(); - LOG(3) << "single getmore: " << ns << endl; long long id = r.d().getInt64( 4 ); @@ -499,6 +514,12 @@ namespace mongo { const string& ns = r.getns(); + AuthorizationManager* authManager = + ClientBasic::getCurrent()->getAuthorizationManager(); + Status status = authManager->checkAuthForInsert(ns); + uassert(16540, status.reason(), status.isOK()); + + int flags = 0; if (d.reservedField() & Reserved_InsertOption_ContinueOnError) flags |= @@ -985,6 +1006,12 @@ namespace mongo { int flags = d.pullInt(); const BSONObj query = d.nextJsObj(); + bool upsert = flags & UpdateOption_Upsert; + AuthorizationManager* authManager = + ClientBasic::getCurrent()->getAuthorizationManager(); + Status status = authManager->checkAuthForUpdate(ns, upsert); + uassert(16537, status.reason(), status.isOK()); + uassert( 10201 , "invalid update" , d.moreJSObjs() ); const BSONObj toUpdate = d.nextJsObj(); @@ -1141,6 +1168,11 @@ namespace mongo { const string& ns = r.getns(); int flags = d.pullInt(); + AuthorizationManager* authManager = + ClientBasic::getCurrent()->getAuthorizationManager(); + Status status = authManager->checkAuthForDelete(ns); + uassert(16541, status.reason(), status.isOK()); + uassert( 10203 , "bad delete message" , d.moreJSObjs() ); const BSONObj query = d.nextJsObj(); @@ -1246,6 +1278,13 @@ namespace mongo { while( d.moreJSObjs() ) { BSONObj o = d.nextJsObj(); const char * ns = o["ns"].valuestr(); + + AuthorizationManager* authManager = + ClientBasic::getCurrent()->getAuthorizationManager(); + uassert(16547, + mongoutils::str::stream() << "not authorized to create index on " << ns, + authManager->checkAuthorization(ns, ActionType::ensureIndex)); + if ( r.getConfig()->isSharded( ns ) ) { BSONObj newIndexKey = o["key"].embeddedObjectUserCheck(); diff --git a/src/mongo/s/strategy_single.cpp b/src/mongo/s/strategy_single.cpp index 4fca33f76da..fe91e66e135 100644 --- a/src/mongo/s/strategy_single.cpp +++ b/src/mongo/s/strategy_single.cpp @@ -133,7 +133,15 @@ namespace mongo { BSONObjBuilder b; vector<Shard> shards; + AuthorizationManager* authManager = + ClientBasic::getCurrent()->getAuthorizationManager(); + if ( strcmp( ns , "inprog" ) == 0 ) { + uassert(16545, + "not authorized to run inprog", + authManager->checkAuthorization(AuthorizationManager::SERVER_RESOURCE_NAME, + ActionType::inprog)); + Shard::getAllShards( shards ); BSONArrayBuilder arr( b.subarrayStart( "inprog" ) ); @@ -172,6 +180,10 @@ namespace mongo { arr.done(); } else if ( strcmp( ns , "killop" ) == 0 ) { + uassert(16546, + "not authorized to run killop", + authManager->checkAuthorization(AuthorizationManager::SERVER_RESOURCE_NAME, + ActionType::killop)); r.checkAuth( Auth::WRITE , "admin" ); BSONElement e = q.query["op"]; |