diff options
-rw-r--r-- | src/mongo/bson/util/bson_check.h | 11 | ||||
-rw-r--r-- | src/mongo/db/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/auth/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session.cpp | 51 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session.h | 11 | ||||
-rw-r--r-- | src/mongo/db/catalog/SConscript | 10 | ||||
-rw-r--r-- | src/mongo/db/commands.cpp | 25 | ||||
-rw-r--r-- | src/mongo/db/commands.h | 57 | ||||
-rw-r--r-- | src/mongo/db/commands/SConscript | 16 | ||||
-rw-r--r-- | src/mongo/db/commands/apply_ops.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/commands/apply_ops_cmd_common.cpp | 165 | ||||
-rw-r--r-- | src/mongo/db/commands/apply_ops_cmd_common.h | 46 | ||||
-rw-r--r-- | src/mongo/db/commands/explain_cmd.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/dbcommands.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/instance.cpp | 8 | ||||
-rw-r--r-- | src/mongo/s/commands/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_explain_cmd.cpp | 8 | ||||
-rw-r--r-- | src/mongo/s/commands/commands_public.cpp | 11 | ||||
-rw-r--r-- | src/mongo/s/s_only.cpp | 2 |
19 files changed, 369 insertions, 81 deletions
diff --git a/src/mongo/bson/util/bson_check.h b/src/mongo/bson/util/bson_check.h index 4f2585e9e75..10a04e005fe 100644 --- a/src/mongo/bson/util/bson_check.h +++ b/src/mongo/bson/util/bson_check.h @@ -81,4 +81,15 @@ Status bsonCheckOnlyHasFields(StringData objectName, return bsonCheckOnlyHasFields(objectName, o, &legals[0], legals + N); } +/** + * Throws a uassert if the type of the elem does not match that provided in expectedType + */ +inline void checkBSONType(BSONType expectedType, const BSONElement& elem) { + uassert(elem.type() == BSONType::EOO ? ErrorCodes::NoSuchKey : ErrorCodes::TypeMismatch, + str::stream() << "Wrong type for '" << elem.fieldNameStringData() << "'. Expected a " + << typeName(expectedType) << ", got a " << typeName(elem.type()) << '.', + elem.type() == expectedType); +} + + } // namespace mongo diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 8932a2f6555..f3012e2a04f 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -517,7 +517,6 @@ serverOnlyFiles = [ "catalog/cursor_manager.cpp", "catalog/database.cpp", "catalog/database_holder.cpp", - "catalog/document_validation.cpp", "catalog/drop_collection.cpp", "catalog/drop_database.cpp", "catalog/drop_indexes.cpp", @@ -655,6 +654,7 @@ serveronlyLibdeps = [ "$BUILD_DIR/third_party/shim_snappy", "auth/authmongod", "catalog/collection_options", + "catalog/document_validation", "catalog/index_key_validate", "commands/killcursors_common", "collection_index_usage_tracker", diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index a0b78e4d3b2..90c7a01e72c 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -36,6 +36,7 @@ env.Library('authcore', ['action_set.cpp', '$BUILD_DIR/mongo/bson/mutable/mutable_bson', '$BUILD_DIR/mongo/bson/util/bson_extract', '$BUILD_DIR/mongo/crypto/scramauth', + '$BUILD_DIR/mongo/db/catalog/document_validation', '$BUILD_DIR/mongo/db/common', '$BUILD_DIR/mongo/db/ops/update_driver', '$BUILD_DIR/mongo/db/namespace_string', diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp index 1007316052b..4f80f19439e 100644 --- a/src/mongo/db/auth/authorization_session.cpp +++ b/src/mongo/db/auth/authorization_session.cpp @@ -42,6 +42,7 @@ #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/privilege.h" #include "mongo/db/auth/security_key.h" +#include "mongo/db/catalog/document_validation.h" #include "mongo/db/client.h" #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" @@ -243,12 +244,14 @@ Status AuthorizationSession::checkAuthForGetMore(const NamespaceString& ns, return Status::OK(); } -Status AuthorizationSession::checkAuthForInsert(const NamespaceString& ns, +Status AuthorizationSession::checkAuthForInsert(OperationContext* txn, + const NamespaceString& ns, const BSONObj& document) { if (ns.coll() == StringData("system.indexes", StringData::LiteralTag())) { BSONElement nsElement = document["ns"]; if (nsElement.type() != String) { - return Status(ErrorCodes::Unauthorized, + return Status(nsElement.type() == BSONType::EOO ? ErrorCodes::NoSuchKey + : ErrorCodes::TypeMismatch, "Cannot authorize inserting into " "system.indexes documents without a string-typed \"ns\" field."); } @@ -258,7 +261,14 @@ Status AuthorizationSession::checkAuthForInsert(const NamespaceString& ns, str::stream() << "not authorized to create index on " << indexNS.ns()); } } else { - if (!isAuthorizedForActionsOnNamespace(ns, ActionType::insert)) { + ActionSet required; + required.addAction(ActionType::insert); + + if (documentValidationDisabled(txn)) { + required.addAction(ActionType::bypassDocumentValidation); + } + + if (!isAuthorizedForActionsOnNamespace(ns, required)) { return Status(ErrorCodes::Unauthorized, str::stream() << "not authorized for insert on " << ns.ns()); } @@ -267,28 +277,35 @@ Status AuthorizationSession::checkAuthForInsert(const NamespaceString& ns, return Status::OK(); } -Status AuthorizationSession::checkAuthForUpdate(const NamespaceString& ns, +Status AuthorizationSession::checkAuthForUpdate(OperationContext* txn, + const NamespaceString& ns, const BSONObj& query, const BSONObj& update, bool upsert) { - if (!upsert) { - if (!isAuthorizedForActionsOnNamespace(ns, ActionType::update)) { - return Status(ErrorCodes::Unauthorized, - str::stream() << "not authorized for update on " << ns.ns()); - } - } else { - ActionSet required; - required.addAction(ActionType::update); + ActionSet required; + required.addAction(ActionType::update); + StringData operationType = "update"; + + if (upsert) { required.addAction(ActionType::insert); - if (!isAuthorizedForActionsOnNamespace(ns, required)) { - return Status(ErrorCodes::Unauthorized, - str::stream() << "not authorized for upsert on " << ns.ns()); - } + operationType = "upsert"; + } + + if (documentValidationDisabled(txn)) { + required.addAction(ActionType::bypassDocumentValidation); } + + if (!isAuthorizedForActionsOnNamespace(ns, required)) { + return Status(ErrorCodes::Unauthorized, + str::stream() << "not authorized for " << operationType << " on " << ns.ns()); + } + return Status::OK(); } -Status AuthorizationSession::checkAuthForDelete(const NamespaceString& ns, const BSONObj& query) { +Status AuthorizationSession::checkAuthForDelete(OperationContext* txn, + const NamespaceString& ns, + const BSONObj& query) { if (!isAuthorizedForActionsOnNamespace(ns, ActionType::remove)) { return Status(ErrorCodes::Unauthorized, str::stream() << "not authorized to remove from " << ns.ns()); diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h index da11de0da2d..7b5ce4799fa 100644 --- a/src/mongo/db/auth/authorization_session.h +++ b/src/mongo/db/auth/authorization_session.h @@ -150,7 +150,8 @@ public: // Checks if this connection has the privileges necessary to perform the given update on the // given namespace. - Status checkAuthForUpdate(const NamespaceString& ns, + Status checkAuthForUpdate(OperationContext* txn, + const NamespaceString& ns, const BSONObj& query, const BSONObj& update, bool upsert); @@ -158,11 +159,15 @@ public: // Checks if this connection has the privileges necessary to insert the given document // to the given namespace. Correctly interprets inserts to system.indexes and performs // the proper auth checks for index building. - Status checkAuthForInsert(const NamespaceString& ns, const BSONObj& document); + Status checkAuthForInsert(OperationContext* txn, + const NamespaceString& ns, + const BSONObj& document); // Checks if this connection has the privileges necessary to perform a delete on the given // namespace. - Status checkAuthForDelete(const NamespaceString& ns, const BSONObj& query); + Status checkAuthForDelete(OperationContext* txn, + const NamespaceString& ns, + const BSONObj& query); // Checks if this connection has the privileges necessary to perform a killCursor on // the identified cursor, supposing that cursor is associated with the supplied namespace diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript index 6640eb268f9..2baa7a6b8e6 100644 --- a/src/mongo/db/catalog/SConscript +++ b/src/mongo/db/catalog/SConscript @@ -8,6 +8,16 @@ env.CppUnitTest('collection_options_test', ['collection_options_test.cpp'], LIBDEPS=['collection_options']) env.Library( + target='document_validation', + source=[ + "document_validation.cpp", + ], + LIBDEPS=[ + "$BUILD_DIR/mongo/db/service_context", + ], +) + +env.Library( target='index_key_validate', source=[ "index_key_validate.cpp", diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index 65560607e50..4b5b6c527ab 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -312,6 +312,12 @@ Status Command::parseCommandCursorOptions(const BSONObj& cmdObj, return Status::OK(); } +Status Command::checkAuthForOperation(OperationContext* txn, + const std::string& dbname, + const BSONObj& cmdObj) { + return checkAuthForCommand(txn->getClient(), dbname, cmdObj); +} + Status Command::checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { @@ -341,16 +347,17 @@ void Command::logIfSlow(const Timer& timer, const string& msg) { } static Status _checkAuthorizationImpl(Command* c, - ClientBasic* client, + OperationContext* txn, const std::string& dbname, const BSONObj& cmdObj) { namespace mmb = mutablebson; + auto client = txn->getClient(); if (c->adminOnly() && dbname != "admin") { return Status(ErrorCodes::Unauthorized, str::stream() << c->name << " may only be run against the admin database."); } if (AuthorizationSession::get(client)->getAuthorizationManager().isAuthEnabled()) { - Status status = c->checkAuthForCommand(client, dbname, cmdObj); + Status status = c->checkAuthForOperation(txn, dbname, cmdObj); if (status == ErrorCodes::Unauthorized) { mmb::Document cmdToLog(cmdObj, mmb::Document::kInPlaceDisabled); c->redactForLogging(&cmdToLog); @@ -370,16 +377,16 @@ static Status _checkAuthorizationImpl(Command* c, return Status::OK(); } -Status Command::_checkAuthorization(Command* c, - ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { +Status Command::checkAuthorization(Command* c, + OperationContext* txn, + const std::string& dbname, + const BSONObj& cmdObj) { namespace mmb = mutablebson; - Status status = _checkAuthorizationImpl(c, client, dbname, cmdObj); + Status status = _checkAuthorizationImpl(c, txn, dbname, cmdObj); if (!status.isOK()) { - log(LogComponent::kAccessControl) << status << std::endl; + log(LogComponent::kAccessControl) << status; } - audit::logCommandAuthzCheck(client, dbname, cmdObj, c, status.code()); + audit::logCommandAuthzCheck(txn->getClient(), dbname, cmdObj, c, status.code()); return status; } diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index 37550c64131..71ba638117b 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -199,12 +199,12 @@ public: } /** - * Checks if the given client is authorized to run this command on database "dbname" - * with the invocation described by "cmdObj". + * Checks if the client associated with the given OperationContext, "txn", is authorized to run + * this command on database "dbname" with the invocation described by "cmdObj". */ - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj); + virtual Status checkAuthForOperation(OperationContext* txn, + const std::string& dbname, + const BSONObj& cmdObj); /** * Redacts "cmdObj" in-place to a form suitable for writing to logs. @@ -259,18 +259,6 @@ public: Command(StringData _name, bool webUI = false, StringData oldName = StringData()); protected: - /** - * Appends to "*out" the privileges required to run this command on database "dbname" with - * the invocation described by "cmdObj". New commands shouldn't implement this, they should - * implement checkAuthForCommand instead. - */ - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - // The default implementation of addRequiredPrivileges should never be hit. - fassertFailed(16940); - } - BSONObj getQuery(const BSONObj& cmdObj) { if (cmdObj["query"].type() == Object) return cmdObj["query"].embeddedObject(); @@ -451,19 +439,40 @@ public: */ static void registerError(OperationContext* txn, const DBException& exception); -private: /** - * Checks to see if the client is authorized to run the given command with the given - * parameters on the given named database. + * Checks to see if the client executing "txn" is authorized to run the given command with the + * given parameters on the given named database. * * Returns Status::OK() if the command is authorized. Most likely returns * ErrorCodes::Unauthorized otherwise, but any return other than Status::OK implies not * authorized. */ - static Status _checkAuthorization(Command* c, - ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj); + static Status checkAuthorization(Command* c, + OperationContext* client, + const std::string& dbname, + const BSONObj& cmdObj); + +private: + /** + * Checks if the given client is authorized to run this command on database "dbname" + * with the invocation described by "cmdObj". + * + * NOTE: Implement checkAuthForOperation that takes an OperationContext* instead. + */ + virtual Status checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj); + /** + * Appends to "*out" the privileges required to run this command on database "dbname" with + * the invocation described by "cmdObj". New commands shouldn't implement this, they should + * implement checkAuthForCommand instead. + */ + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + // The default implementation of addRequiredPrivileges should never be hit. + fassertFailed(16940); + } }; void runCommands(OperationContext* txn, diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index d36f661e414..b4e3cebf04d 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -48,6 +48,7 @@ env.Library( '$BUILD_DIR/mongo/db/auth/authorization_manager_global', '$BUILD_DIR/mongo/db/auth/serverauth', '$BUILD_DIR/mongo/db/commands', + '$BUILD_DIR/mongo/db/commands/apply_ops_cmd_common', '$BUILD_DIR/mongo/db/commands/test_commands_enabled', '$BUILD_DIR/mongo/db/common', '$BUILD_DIR/mongo/db/curop', @@ -95,6 +96,21 @@ env.Library( ], ) +env.Library( + target='apply_ops_cmd_common', + source=[ + 'apply_ops_cmd_common.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/catalog/document_validation', + '$BUILD_DIR/mongo/db/commands', + '$BUILD_DIR/mongo/db/auth/authcore', + '$BUILD_DIR/mongo/db/namespace_string', + '$BUILD_DIR/mongo/db/service_context', + ], +) + env.CppUnitTest( target="index_filter_commands_test", source=[ diff --git a/src/mongo/db/commands/apply_ops.cpp b/src/mongo/db/commands/apply_ops.cpp index 12dc70ceb86..04e9117b548 100644 --- a/src/mongo/db/commands/apply_ops.cpp +++ b/src/mongo/db/commands/apply_ops.cpp @@ -37,13 +37,12 @@ #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_manager_global.h" #include "mongo/db/auth/authorization_session.h" -#include "mongo/db/auth/privilege.h" -#include "mongo/db/auth/resource_pattern.h" #include "mongo/db/catalog/apply_ops.h" #include "mongo/db/catalog/document_validation.h" #include "mongo/db/client.h" #include "mongo/db/commands.h" #include "mongo/db/commands/dbhash.h" +#include "mongo/db/commands/apply_ops_cmd_common.h" #include "mongo/db/db_raii.h" #include "mongo/db/concurrency/write_conflict_exception.h" #include "mongo/db/dbdirectclient.h" @@ -78,12 +77,13 @@ public: help << "internal (sharding)\n{ applyOps : [ ] , preCondition : [ { ns : ... , q : ... , " "res : ... } ] }"; } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - // applyOps can do pretty much anything, so require all privileges. - RoleGraph::generateUniversalPrivileges(out); + + virtual Status checkAuthForOperation(OperationContext* txn, + const std::string& dbname, + const BSONObj& cmdObj) final { + return checkAuthForApplyOpsCommand(txn, dbname, cmdObj); } + virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, diff --git a/src/mongo/db/commands/apply_ops_cmd_common.cpp b/src/mongo/db/commands/apply_ops_cmd_common.cpp new file mode 100644 index 00000000000..045570abf8a --- /dev/null +++ b/src/mongo/db/commands/apply_ops_cmd_common.cpp @@ -0,0 +1,165 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#include "mongo/db/commands/apply_ops_cmd_common.h" + +#include "mongo/base/status.h" +#include "mongo/base/string_data.h" +#include "mongo/bson/bsonobj.h" +#include "mongo/bson/util/bson_check.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/privilege.h" +#include "mongo/db/auth/resource_pattern.h" +#include "mongo/db/catalog/document_validation.h" +#include "mongo/db/client.h" +#include "mongo/db/commands.h" +#include "mongo/db/namespace_string.h" + +namespace mongo { + +namespace { + +Status checkOperationAuthorization(OperationContext* txn, + const std::string& dbname, + const BSONObj& oplogEntry, + bool alwaysUpsert) { + AuthorizationSession* authSession = AuthorizationSession::get(txn->getClient()); + + BSONElement opTypeElem = oplogEntry["op"]; + checkBSONType(BSONType::String, opTypeElem); + const StringData opType = opTypeElem.checkAndGetStringData(); + + if (opType == "n") { + // oplog notes require cluster permissions, and may not have a ns + if (!authSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), + ActionType::appendOplogNote)) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + return Status::OK(); + } + + BSONElement nsElem = oplogEntry["ns"]; + checkBSONType(BSONType::String, nsElem); + NamespaceString ns(oplogEntry["ns"].checkAndGetStringData()); + + BSONElement oElem = oplogEntry["o"]; + checkBSONType(BSONType::Object, oElem); + BSONObj o = oElem.Obj(); + + if (opType == "c") { + Command* command = Command::findCommand(o.firstElement().fieldNameStringData()); + if (!command) { + return Status(ErrorCodes::FailedToParse, "Unrecognized command in op"); + } + + return Command::checkAuthorization(command, txn, dbname, o); + } + + if (opType == "i") { + return authSession->checkAuthForInsert(txn, ns, o); + } else if (opType == "u") { + BSONElement o2Elem = oplogEntry["o2"]; + checkBSONType(BSONType::Object, o2Elem); + BSONObj o2 = o2Elem.Obj(); + + BSONElement bElem = oplogEntry["b"]; + if (!bElem.eoo()) { + checkBSONType(BSONType::Bool, bElem); + } + bool b = bElem.trueValue(); + + const bool upsert = b || alwaysUpsert; + + return authSession->checkAuthForUpdate(txn, ns, o, o2, upsert); + } else if (opType == "d") { + return authSession->checkAuthForDelete(txn, ns, o); + } else if (opType == "db") { + // It seems that 'db' isn't used anymore. Require all actions to prevent casual use. + ActionSet allActions; + allActions.addAllActions(); + if (!authSession->isAuthorizedForActionsOnResource(ResourcePattern::forAnyResource(), + allActions)) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + return Status::OK(); + } + + return Status(ErrorCodes::FailedToParse, "Unrecognized opType"); +} + +} // namespace + + +Status checkAuthForApplyOpsCommand(OperationContext* txn, + const std::string& dbname, + const BSONObj& cmdObj) { + AuthorizationSession* authSession = AuthorizationSession::get(txn->getClient()); + + + std::vector<Privilege> universalPrivileges; + RoleGraph::generateUniversalPrivileges(&universalPrivileges); + if (!authSession->isAuthorizedForPrivileges(universalPrivileges)) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + + + boost::optional<DisableDocumentValidation> maybeDisableValidation; + if (shouldBypassDocumentValidationForCommand(cmdObj)) + maybeDisableValidation.emplace(txn); + + + const bool alwaysUpsert = + cmdObj.hasField("alwaysUpsert") ? cmdObj["alwaysUpsert"].trueValue() : true; + + checkBSONType(BSONType::Array, cmdObj.firstElement()); + for (const BSONElement& e : cmdObj.firstElement().Array()) { + checkBSONType(BSONType::Object, e); + Status status = checkOperationAuthorization(txn, dbname, e.Obj(), alwaysUpsert); + if (!status.isOK()) { + return status; + } + } + + BSONElement preconditions = cmdObj["preCondition"]; + if (!preconditions.eoo()) { + for (const BSONElement& precondition : preconditions.Array()) { + checkBSONType(BSONType::Object, precondition); + BSONElement nsElem = precondition.Obj()["ns"]; + checkBSONType(BSONType::String, nsElem); + NamespaceString nss(nsElem.checkAndGetStringData()); + + if (!authSession->isAuthorizedForActionsOnResource( + ResourcePattern::forExactNamespace(nss), ActionType::find)) { + return Status(ErrorCodes::Unauthorized, "Unauthorized to check precondition"); + } + } + } + + return Status::OK(); +} +} // namespace mongo diff --git a/src/mongo/db/commands/apply_ops_cmd_common.h b/src/mongo/db/commands/apply_ops_cmd_common.h new file mode 100644 index 00000000000..2b4856f3f74 --- /dev/null +++ b/src/mongo/db/commands/apply_ops_cmd_common.h @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#pragma once + +#include <string> + +namespace mongo { + +class BSONObj; +class OperationContext; +class Status; + +/** + * Returns Status::OK if the associated client is authorized to perform the command in cmdObj. + */ +Status checkAuthForApplyOpsCommand(OperationContext* txn, + const std::string& dbname, + const BSONObj& cmdObj); + +} // namespace mongo diff --git a/src/mongo/db/commands/explain_cmd.cpp b/src/mongo/db/commands/explain_cmd.cpp index 2af6f18fed5..ffae7056bc7 100644 --- a/src/mongo/db/commands/explain_cmd.cpp +++ b/src/mongo/db/commands/explain_cmd.cpp @@ -88,9 +88,9 @@ public: * the command that you are explaining. The auth check is performed recursively * on the nested command. */ - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { + virtual Status checkAuthForOperation(OperationContext* txn, + const std::string& dbname, + const BSONObj& cmdObj) { if (Object != cmdObj.firstElement().type()) { return Status(ErrorCodes::BadValue, "explain command requires a nested object"); } @@ -104,7 +104,7 @@ public: return Status(ErrorCodes::CommandNotFound, ss); } - return commToExplain->checkAuthForCommand(client, dbname, explainObj); + return commToExplain->checkAuthForOperation(txn, dbname, explainObj); } virtual bool run(OperationContext* txn, diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index f2b87b6c399..e65da17dd07 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -1230,8 +1230,7 @@ void Command::execCommand(OperationContext* txn, } ImpersonationSessionGuard guard(txn); - uassertStatusOK( - _checkAuthorization(command, txn->getClient(), dbname, request.getCommandArgs())); + uassertStatusOK(checkAuthorization(command, txn, dbname, request.getCommandArgs())); repl::ReplicationCoordinator* replCoord = repl::ReplicationCoordinator::get(txn->getClient()->getServiceContext()); diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp index 4f393385127..86576565532 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -669,8 +669,8 @@ void receivedUpdate(OperationContext* txn, const NamespaceString& nsString, Mess op.setQuery_inlock(query); } - Status status = - AuthorizationSession::get(client)->checkAuthForUpdate(nsString, query, toupdate, upsert); + Status status = AuthorizationSession::get(client) + ->checkAuthForUpdate(txn, nsString, query, toupdate, upsert); audit::logUpdateAuthzCheck(client, nsString, query, toupdate, upsert, multi, status.code()); uassertStatusOK(status); @@ -818,7 +818,7 @@ void receivedDelete(OperationContext* txn, const NamespaceString& nsString, Mess op.setNS_inlock(nsString.ns()); } - Status status = AuthorizationSession::get(client)->checkAuthForDelete(nsString, pattern); + Status status = AuthorizationSession::get(client)->checkAuthForDelete(txn, nsString, pattern); audit::logDeleteAuthzCheck(client, nsString, pattern, status.code()); uassertStatusOK(status); @@ -1181,7 +1181,7 @@ void receivedInsert(OperationContext* txn, const NamespaceString& nsString, Mess // Check auth for insert (also handles checking if this is an index build and checks // for the proper privileges in that case). Status status = - AuthorizationSession::get(txn->getClient())->checkAuthForInsert(nsString, obj); + AuthorizationSession::get(txn->getClient())->checkAuthForInsert(txn, nsString, obj); audit::logInsertAuthzCheck(txn->getClient(), nsString, obj, status.code()); uassertStatusOK(status); } diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript index 3a988888202..5400114f85d 100644 --- a/src/mongo/s/commands/SConscript +++ b/src/mongo/s/commands/SConscript @@ -65,6 +65,7 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/client/parallel', + '$BUILD_DIR/mongo/db/commands/apply_ops_cmd_common', '$BUILD_DIR/mongo/db/pipeline/pipeline', '$BUILD_DIR/mongo/db/commands/killcursors_common', '$BUILD_DIR/mongo/s/coreshard', diff --git a/src/mongo/s/commands/cluster_explain_cmd.cpp b/src/mongo/s/commands/cluster_explain_cmd.cpp index a48076dc93a..0e1353bd9fc 100644 --- a/src/mongo/s/commands/cluster_explain_cmd.cpp +++ b/src/mongo/s/commands/cluster_explain_cmd.cpp @@ -87,9 +87,9 @@ public: * the command that you are explaining. The auth check is performed recursively * on the nested command. */ - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { + virtual Status checkAuthForOperation(OperationContext* txn, + const std::string& dbname, + const BSONObj& cmdObj) { if (Object != cmdObj.firstElement().type()) { return Status(ErrorCodes::BadValue, "explain command requires a nested object"); } @@ -103,7 +103,7 @@ public: return Status(ErrorCodes::CommandNotFound, ss); } - return commToExplain->checkAuthForCommand(client, dbname, explainObj); + return commToExplain->checkAuthForOperation(txn, dbname, explainObj); } virtual bool run(OperationContext* txn, diff --git a/src/mongo/s/commands/commands_public.cpp b/src/mongo/s/commands/commands_public.cpp index 6737fe47674..5d7ea13a58d 100644 --- a/src/mongo/s/commands/commands_public.cpp +++ b/src/mongo/s/commands/commands_public.cpp @@ -39,6 +39,7 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/privilege.h" #include "mongo/db/commands.h" +#include "mongo/db/commands/apply_ops_cmd_common.h" #include "mongo/db/commands/copydb.h" #include "mongo/db/commands/rename_collection.h" #include "mongo/db/lasterror.h" @@ -1371,11 +1372,11 @@ public: class ApplyOpsCmd : public PublicGridCommand { public: ApplyOpsCmd() : PublicGridCommand("applyOps") {} - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - // applyOps can do pretty much anything, so require all privileges. - RoleGraph::generateUniversalPrivileges(out); + + virtual Status checkAuthForOperation(OperationContext* txn, + const std::string& dbname, + const BSONObj& cmdObj) { + return checkAuthForApplyOpsCommand(txn, dbname, cmdObj); } virtual bool run(OperationContext* txn, const string& dbName, diff --git a/src/mongo/s/s_only.cpp b/src/mongo/s/s_only.cpp index 2322aa67d3d..3cf43adb15c 100644 --- a/src/mongo/s/s_only.cpp +++ b/src/mongo/s/s_only.cpp @@ -110,7 +110,7 @@ void Command::execCommandClientBasic(OperationContext* txn, return; } - Status status = _checkAuthorization(c, &client, dbname, cmdObj); + Status status = checkAuthorization(c, txn, dbname, cmdObj); if (!status.isOK()) { appendCommandStatus(result, status); return; |