diff options
author | Spencer Jackson <spencer.jackson@mongodb.com> | 2016-08-10 14:39:34 -0400 |
---|---|---|
committer | Spencer Jackson <spencer.jackson@mongodb.com> | 2016-08-10 14:39:34 -0400 |
commit | f6bb7b0a3bff0495db6be1e04ff72d95862cbb6f (patch) | |
tree | f8b9cc8250552dc7d2602451c7f84e64ba84d25e /src | |
parent | df2dacb8a92ac0a5f42b7e40a848c8074940ef2d (diff) | |
download | mongo-f6bb7b0a3bff0495db6be1e04ff72d95862cbb6f.tar.gz |
Revert "SERVER-22826 Support X509 Authorization"
This reverts commit d930f4832631eca7092ada4328d780f2b8d19d31.
Diffstat (limited to 'src')
40 files changed, 236 insertions, 851 deletions
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index 555d21845ea..93a2091e343 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -10,12 +10,6 @@ generateActionTypes = env.Command( action='$PYTHON $SOURCES $TARGETS') env.Alias('generated-sources', generateActionTypes) -env.Library('auth_rolename', ['role_name.cpp'], - LIBDEPS=[ - '$BUILD_DIR/mongo/base', - ] -) - # Just the data structures used env.Library('authcore', ['action_set.cpp', 'action_type.cpp', @@ -30,14 +24,14 @@ env.Library('authcore', ['action_set.cpp', 'resource_pattern.cpp', 'role_graph.cpp', 'role_graph_update.cpp', + 'role_name.cpp', 'role_graph_builtin_roles.cpp', 'user.cpp', 'user_document_parser.cpp', 'user_management_commands_parser.cpp', 'user_name.cpp', 'user_set.cpp'], - LIBDEPS=['auth_rolename', - 'sasl_options', + LIBDEPS=['sasl_options', '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/bson/mutable/mutable_bson', '$BUILD_DIR/mongo/bson/util/bson_extract', @@ -180,11 +174,6 @@ env.CppUnitTest('user_document_parser_test', 'user_document_parser_test.cpp', env.CppUnitTest('user_set_test', 'user_set_test.cpp', LIBDEPS=['authcore', 'authmocks', 'saslauth']) env.CppUnitTest('authorization_manager_test', 'authorization_manager_test.cpp', - LIBDEPS=[ - '$BUILD_DIR/mongo/transport/transport_layer_common', - '$BUILD_DIR/mongo/transport/transport_layer_mock', - 'authcore', - 'authmocks', - 'saslauth']) + LIBDEPS=['authcore', 'authmocks', 'saslauth']) env.CppUnitTest('authorization_session_test', 'authorization_session_test.cpp', LIBDEPS=['authcore', 'authmocks', 'saslauth']) diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 8f404e7ed8c..131eeda34c6 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -422,26 +422,18 @@ Status AuthorizationManager::getUserDescription(OperationContext* txn, Status AuthorizationManager::getRoleDescription(OperationContext* txn, const RoleName& roleName, - PrivilegeFormat privileges, + bool showPrivileges, BSONObj* result) { - return _externalState->getRoleDescription(txn, roleName, privileges, result); + return _externalState->getRoleDescription(txn, roleName, showPrivileges, result); } -Status AuthorizationManager::getRolesDescription(OperationContext* txn, - const std::vector<RoleName>& roleName, - PrivilegeFormat privileges, - BSONObj* result) { - return _externalState->getRolesDescription(txn, roleName, privileges, result); -} - - Status AuthorizationManager::getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - PrivilegeFormat privileges, + bool showPrivileges, bool showBuiltinRoles, vector<BSONObj>* result) { return _externalState->getRoleDescriptionsForDB( - txn, dbname, privileges, showBuiltinRoles, result); + txn, dbname, showPrivileges, showBuiltinRoles, result); } Status AuthorizationManager::acquireUser(OperationContext* txn, diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index b12abccad40..43b1741841b 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -36,7 +36,6 @@ #include "mongo/bson/mutable/element.h" #include "mongo/bson/oid.h" #include "mongo/db/auth/action_set.h" -#include "mongo/db/auth/privilege_format.h" #include "mongo/db/auth/resource_pattern.h" #include "mongo/db/auth/role_graph.h" #include "mongo/db/auth/user.h" @@ -220,32 +219,50 @@ public: ActionSet getActionsForOldStyleUser(const std::string& dbname, bool readOnly) const; /** - * Delegates method call to the underlying AuthzManagerExternalState. + * Writes into "result" a document describing the named user and returns Status::OK(). The + * description includes the user credentials and customData, if present, the user's role + * membership and delegation information, a full list of the user's privileges, and a full + * list of the user's roles, including those roles held implicitly through other roles + * (indirect roles). In the event that some of this information is inconsistent, the + * document will contain a "warnings" array, with std::string messages describing + * inconsistencies. + * + * If the user does not exist, returns ErrorCodes::UserNotFound. */ Status getUserDescription(OperationContext* txn, const UserName& userName, BSONObj* result); /** - * Delegates method call to the underlying AuthzManagerExternalState. + * Writes into "result" a document describing the named role and returns Status::OK(). The + * description includes the roles in which the named role has membership and a full list of + * the roles of which the named role is a member, including those roles memberships held + * implicitly through other roles (indirect roles). If "showPrivileges" is true, then the + * description documents will also include a full list of the role's privileges. + * In the event that some of this information is inconsistent, the document will contain a + * "warnings" array, with std::string messages describing inconsistencies. + * + * If the role does not exist, returns ErrorCodes::RoleNotFound. */ Status getRoleDescription(OperationContext* txn, const RoleName& roleName, - PrivilegeFormat privilegeFormat, + bool showPrivileges, BSONObj* result); /** - * Delegates method call to the underlying AuthzManagerExternalState. - */ - Status getRolesDescription(OperationContext* txn, - const std::vector<RoleName>& roleName, - PrivilegeFormat privilegeFormat, - BSONObj* result); - - /** - * Delegates method call to the underlying AuthzManagerExternalState. + * Writes into "result" documents describing the roles that are defined on the given + * database. Each role description document includes the other roles in which the role has + * membership and a full list of the roles of which the named role is a member, + * including those roles memberships held implicitly through other roles (indirect roles). + * If showPrivileges is true, then the description documents will also include a full list + * of the role's privileges. If showBuiltinRoles is true, then the result array will + * contain description documents for all the builtin roles for the given database, if it + * is false the result will just include user defined roles. + * In the event that some of the information in a given role description is inconsistent, + * the document will contain a "warnings" array, with std::string messages describing + * inconsistencies. */ Status getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - PrivilegeFormat privilegeFormat, + bool showPrivileges, bool showBuiltinRoles, std::vector<BSONObj>* result); diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp index 56afbd26d40..cd2b83fa6b1 100644 --- a/src/mongo/db/auth/authorization_manager_test.cpp +++ b/src/mongo/db/auth/authorization_manager_test.cpp @@ -41,13 +41,9 @@ #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context_noop.h" -#include "mongo/db/service_context_noop.h" #include "mongo/stdx/memory.h" -#include "mongo/transport/session.h" -#include "mongo/transport/transport_layer_mock.h" #include "mongo/unittest/unittest.h" #include "mongo/util/map_util.h" -#include "mongo/util/net/message_port.h" #define ASSERT_NULL(EXPR) ASSERT_FALSE(EXPR) #define ASSERT_NON_NULL(EXPR) ASSERT_TRUE(EXPR) @@ -241,65 +237,6 @@ TEST_F(AuthorizationManagerTest, testAcquireV2User) { authzManager->releaseUser(v2cluster); } -TEST_F(AuthorizationManagerTest, testLocalX509Authorization) { - ServiceContextNoop serviceContext; - transport::TransportLayerMock transportLayer{}; - transport::Session* session = transportLayer.createSession(); - transportLayer.setX509PeerInfo( - *session, - SSLPeerInfo("CN=mongodb.com", {RoleName("read", "test"), RoleName("readWrite", "test")})); - ServiceContext::UniqueClient client = serviceContext.makeClient("testClient", session); - ServiceContext::UniqueOperationContext txn = client->makeOperationContext(); - - User* x509User; - ASSERT_OK( - authzManager->acquireUser(txn.get(), UserName("CN=mongodb.com", "$external"), &x509User)); - ASSERT(x509User->isValid()); - - RoleNameIterator roles = x509User->getRoles(); - ASSERT_EQUALS(RoleName("read", "test"), roles.next()); - ASSERT_EQUALS(RoleName("readWrite", "test"), roles.next()); - ASSERT_FALSE(roles.more()); - - - const User::ResourcePrivilegeMap& privileges = x509User->getPrivileges(); - ASSERT_FALSE(privileges.empty()); - auto privilegeIt = privileges.find(ResourcePattern::forDatabaseName("test")); - ASSERT(privilegeIt != privileges.end()); - ASSERT(privilegeIt->second.includesAction(ActionType::insert)); - - - authzManager->releaseUser(x509User); -} - -TEST_F(AuthorizationManagerTest, testLocalX509AuthorizationInvalidUser) { - ServiceContextNoop serviceContext; - transport::TransportLayerMock transportLayer{}; - transport::Session* session = transportLayer.createSession(); - transportLayer.setX509PeerInfo( - *session, - SSLPeerInfo("CN=mongodb.com", {RoleName("read", "test"), RoleName("write", "test")})); - ServiceContext::UniqueClient client = serviceContext.makeClient("testClient", session); - ServiceContext::UniqueOperationContext txn = client->makeOperationContext(); - - User* x509User; - ASSERT_NOT_OK( - authzManager->acquireUser(txn.get(), UserName("CN=10gen.com", "$external"), &x509User)); -} - -TEST_F(AuthorizationManagerTest, testLocalX509AuthenticationNoAuthorization) { - ServiceContextNoop serviceContext; - transport::TransportLayerMock transportLayer{}; - transport::Session* session = transportLayer.createSession(); - transportLayer.setX509PeerInfo(*session, {}); - ServiceContext::UniqueClient client = serviceContext.makeClient("testClient", session); - ServiceContext::UniqueOperationContext txn = client->makeOperationContext(); - - User* x509User; - ASSERT_NOT_OK( - authzManager->acquireUser(txn.get(), UserName("CN=mongodb.com", "$external"), &x509User)); -} - /** * An implementation of AuthzManagerExternalStateMock that overrides the getUserDescription method * to return the user document unmodified from how it was inserted. When using this insert user diff --git a/src/mongo/db/auth/authz_manager_external_state.cpp b/src/mongo/db/auth/authz_manager_external_state.cpp index ed5f0fe6bfd..fae54cbf5e9 100644 --- a/src/mongo/db/auth/authz_manager_external_state.cpp +++ b/src/mongo/db/auth/authz_manager_external_state.cpp @@ -29,9 +29,6 @@ #include "mongo/platform/basic.h" #include "mongo/db/auth/authz_manager_external_state.h" -#include "mongo/db/auth/user_name.h" -#include "mongo/db/operation_context.h" -#include "mongo/util/net/ssl_types.h" namespace mongo { @@ -40,13 +37,4 @@ stdx::function<std::unique_ptr<AuthzManagerExternalState>()> AuthzManagerExterna AuthzManagerExternalState::AuthzManagerExternalState() = default; AuthzManagerExternalState::~AuthzManagerExternalState() = default; -bool AuthzManagerExternalState::shouldUseRolesFromConnection(OperationContext* txn, - const UserName& userName) { - return txn && txn->getClient() && txn->getClient()->session() && - txn->getClient()->session()->getX509PeerInfo().subjectName == userName.getUser() && - userName.getDB() == "$external" && - !txn->getClient()->session()->getX509PeerInfo().roles.empty(); -} - - } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h index f3d2cc721c6..78aac5ea832 100644 --- a/src/mongo/db/auth/authz_manager_external_state.h +++ b/src/mongo/db/auth/authz_manager_external_state.h @@ -34,9 +34,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" -#include "mongo/db/auth/privilege_format.h" #include "mongo/db/auth/role_name.h" -#include "mongo/db/auth/user.h" #include "mongo/db/auth/user_name.h" #include "mongo/db/jsobj.h" #include "mongo/stdx/functional.h" @@ -82,12 +80,11 @@ public: /** * Writes into "result" a document describing the named user and returns Status::OK(). The - * description includes the user credentials and customData, if present, the user's role - * membership and delegation information, a full list of the user's privileges, and a full - * list of the user's roles, including those roles held implicitly through other roles - * (indirect roles). In the event that some of this information is inconsistent, the - * document will contain a "warnings" array, with std::string messages describing - * inconsistencies. + * description includes the user credentials, if present, the user's role membership and + * delegation information, a full list of the user's privileges, and a full list of the + * user's roles, including those roles held implicitly through other roles (indirect roles). + * In the event that some of this information is inconsistent, the document will contain a + * "warnings" array, with std::string messages describing inconsistencies. * * If the user does not exist, returns ErrorCodes::UserNotFound. */ @@ -96,57 +93,37 @@ public: BSONObj* result) = 0; /** - * Writes into "result" a document describing the named role and returns Status::OK(). If - * showPrivileges is kOmit or kShowPrivileges, the description includes the roles which the - * named role is a member of, including those memberships held implicitly through other roles - * (indirect roles). If "showPrivileges" is kShowPrivileges, then the description documents - * will also include a full list of the role's privileges. If "showPrivileges" is - * kShowAsUserFragment, then the description returned will take the form of a partial user - * document, describing a hypothetical user which possesses the provided and implicit roles, - * and all inherited privileges. In the event that some of this information is inconsistent, - * the document will contain a "warnings" array, with std::string messages describing - * inconsistencies. + * Writes into "result" a document describing the named role and returns Status::OK(). The + * description includes the roles in which the named role has membership and a full list of + * the roles of which the named role is a member, including those roles memberships held + * implicitly through other roles (indirect roles). If "showPrivileges" is true, then the + * description documents will also include a full list of the role's privileges. + * In the event that some of this information is inconsistent, the document will contain a + * "warnings" array, with std::string messages describing inconsistencies. * * If the role does not exist, returns ErrorCodes::RoleNotFound. */ virtual Status getRoleDescription(OperationContext* txn, const RoleName& roleName, - PrivilegeFormat showPrivileges, + bool showPrivileges, BSONObj* result) = 0; /** - * Writes into "result" a document describing the named role is and returns Status::OK(). If - * showPrivileges is kOmit or kShowPrivileges, the description includes the roles which the - * named roles are a member of, including those memberships held implicitly through other roles - * (indirect roles). If "showPrivileges" is kShowPrivileges, then the description documents - * will also include a full list of the roles' privileges. If "showPrivileges" is - * kShowAsUserFragment, then the description returned will take the form of a partial user - * document, describing a hypothetical user which possesses the provided and implicit roles, - * and all inherited privileges. In the event that some of this information is inconsistent, + * Writes into "result" documents describing the roles that are defined on the given + * database. Each role description document includes the other roles in which the role has + * membership and a full list of the roles of which the named role is a member, + * including those roles memberships held implicitly through other roles (indirect roles). + * If showPrivileges is true, then the description documents will also include a full list + * of the role's privileges. If showBuiltinRoles is true, then the result array will + * contain description documents for all the builtin roles for the given database, if it + * is false the result will just include user defined roles. + * In the event that some of the information in a given role description is inconsistent, * the document will contain a "warnings" array, with std::string messages describing * inconsistencies. */ - - virtual Status getRolesDescription(OperationContext* txn, - const std::vector<RoleName>& roles, - PrivilegeFormat showPrivileges, - BSONObj* result) = 0; - - /** - * Writes into "result" documents describing the roles that are defined on the given - * database. If showPrivileges is kOmit or kShowPrivileges, then a vector of BSON documents are - * returned, where each document includes the other roles a particular role is a - * member of, including those role memberships held implicitly through other roles - * (indirect roles). If showPrivileges is kShowPrivileges, then the description documents - * will also include a full list of the roles' privileges. If showBuiltinRoles is true, then - * the result array will contain description documents for all the builtin roles for the given - * database, if it is false the result will just include user defined roles. In the event that - * some of the information in a given role description is inconsistent, the document will - * contain a "warnings" array, with std::string messages describing inconsistencies. - */ virtual Status getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - PrivilegeFormat showPrivileges, + bool showPrivileges, bool showBuiltinRoles, std::vector<BSONObj>* result) = 0; @@ -164,12 +141,6 @@ public: protected: AuthzManagerExternalState(); // This class should never be instantiated directly. - - /** - * Returns true if roles for this user were provided by the client, and can be obtained from - * the connection. - */ - bool shouldUseRolesFromConnection(OperationContext* txn, const UserName& username); }; } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state_local.cpp b/src/mongo/db/auth/authz_manager_external_state_local.cpp index d4f814baae5..82bd5c29440 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp @@ -152,25 +152,9 @@ bool AuthzManagerExternalStateLocal::hasAnyPrivilegeDocuments(OperationContext* Status AuthzManagerExternalStateLocal::getUserDescription(OperationContext* txn, const UserName& userName, BSONObj* result) { - Status status = Status::OK(); - - if (!shouldUseRolesFromConnection(txn, userName)) { - status = _getUserDocument(txn, userName, result); - if (!status.isOK()) - return status; - } else { - // We are able to artifically construct the external user from the request - BSONArrayBuilder userRoles; - for (const RoleName& role : txn->getClient()->session()->getX509PeerInfo().roles) { - userRoles << BSON("role" << role.getRole() << "db" << role.getDB()); - } - *result = BSON("_id" << userName.getUser() << "user" << userName.getUser() << "db" - << userName.getDB() - << "credentials" - << BSON("external" << true) - << "roles" - << userRoles.arr()); - } + Status status = _getUserDocument(txn, userName, result); + if (!status.isOK()) + return status; BSONElement directRolesElement; status = bsonExtractTypedField(*result, "roles", Array, &directRolesElement); @@ -257,55 +241,14 @@ Status AuthzManagerExternalStateLocal::_getUserDocument(OperationContext* txn, Status AuthzManagerExternalStateLocal::getRoleDescription(OperationContext* txn, const RoleName& roleName, - PrivilegeFormat showPrivileges, + bool showPrivileges, BSONObj* result) { - if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { - mutablebson::Document resultDoc; - mutablebson::Element rolesElement = resultDoc.makeElementArray("roles"); - fassert(40271, resultDoc.root().pushBack(rolesElement)); - addRoleNameObjectsToArrayElement( - rolesElement, makeRoleNameIteratorForContainer(std::vector<RoleName>{roleName})); - resolveUserRoles(&resultDoc, {roleName}); - *result = resultDoc.getObject(); - return Status::OK(); - } stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex); return _getRoleDescription_inlock(roleName, showPrivileges, result); } -Status AuthzManagerExternalStateLocal::getRolesDescription(OperationContext* txn, - const std::vector<RoleName>& roles, - PrivilegeFormat showPrivileges, - BSONObj* result) { - if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { - mutablebson::Document resultDoc; - mutablebson::Element rolesElement = resultDoc.makeElementArray("roles"); - fassert(40272, resultDoc.root().pushBack(rolesElement)); - addRoleNameObjectsToArrayElement(rolesElement, makeRoleNameIteratorForContainer(roles)); - resolveUserRoles(&resultDoc, roles); - *result = resultDoc.getObject(); - return Status::OK(); - } - - stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex); - BSONArrayBuilder resultBuilder; - for (const RoleName& role : roles) { - BSONObj roleDoc; - Status status = _getRoleDescription_inlock(role, showPrivileges, &roleDoc); - if (!status.isOK()) { - if (status.code() == ErrorCodes::RoleNotFound) { - continue; - } - return status; - } - resultBuilder << roleDoc; - } - *result = resultBuilder.arr(); - return Status::OK(); -} - Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName& roleName, - PrivilegeFormat showPrivileges, + bool showPrivileges, BSONObj* result) { if (!_roleGraph.roleExists(roleName)) return Status(ErrorCodes::RoleNotFound, "No role named " + roleName.toString()); @@ -325,7 +268,7 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName mutablebson::Element privilegesElement = resultDoc.makeElementArray("privileges"); mutablebson::Element inheritedPrivilegesElement = resultDoc.makeElementArray("inheritedPrivileges"); - if (showPrivileges == PrivilegeFormat::kShowSeparate) { + if (showPrivileges) { fassert(17166, resultDoc.root().pushBack(privilegesElement)); } mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings"); @@ -334,7 +277,7 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName if (_roleGraphState == roleGraphStateConsistent) { addRoleNameObjectsToArrayElement(inheritedRolesElement, _roleGraph.getIndirectSubordinates(roleName)); - if (showPrivileges == PrivilegeFormat::kShowSeparate) { + if (showPrivileges) { addPrivilegeObjectsOrWarningsToArrayElement( privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName)); @@ -343,7 +286,7 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName fassert(17323, resultDoc.root().pushBack(inheritedPrivilegesElement)); } - } else if (showPrivileges == PrivilegeFormat::kShowSeparate) { + } else if (showPrivileges) { warningsElement.appendString( "", "Role graph state inconsistent; only direct privileges available."); addPrivilegeObjectsOrWarningsToArrayElement( @@ -358,15 +301,11 @@ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName Status AuthzManagerExternalStateLocal::getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - PrivilegeFormat showPrivileges, + bool showPrivileges, bool showBuiltinRoles, vector<BSONObj>* result) { - if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { - return Status(ErrorCodes::IllegalOperation, - "Cannot get user fragment for all roles in a database"); - } - stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex); + for (RoleNameIterator it = _roleGraph.getRolesForDatabase(dbname); it.more(); it.next()) { if (!showBuiltinRoles && _roleGraph.isBuiltinRole(it.get())) { continue; diff --git a/src/mongo/db/auth/authz_manager_external_state_local.h b/src/mongo/db/auth/authz_manager_external_state_local.h index e1e2cbdc1e2..c9646686b03 100644 --- a/src/mongo/db/auth/authz_manager_external_state_local.h +++ b/src/mongo/db/auth/authz_manager_external_state_local.h @@ -63,15 +63,11 @@ public: BSONObj* result); virtual Status getRoleDescription(OperationContext* txn, const RoleName& roleName, - PrivilegeFormat showPrivileges, + bool showPrivileges, BSONObj* result); - virtual Status getRolesDescription(OperationContext* txn, - const std::vector<RoleName>& roles, - PrivilegeFormat showPrivileges, - BSONObj* result); virtual Status getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - PrivilegeFormat showPrivileges, + bool showPrivileges, bool showBuiltinRoles, std::vector<BSONObj>* result); @@ -135,7 +131,7 @@ private: Status _getUserDocument(OperationContext* txn, const UserName& userName, BSONObj* result); Status _getRoleDescription_inlock(const RoleName& roleName, - PrivilegeFormat showPrivileges, + bool showPrivileges, BSONObj* result); /** * Eventually consistent, in-memory representation of all roles in the system (both diff --git a/src/mongo/db/auth/authz_manager_external_state_s.cpp b/src/mongo/db/auth/authz_manager_external_state_s.cpp index 06831cf9a58..500cc85c70a 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -38,47 +38,16 @@ #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_manager_global.h" #include "mongo/db/auth/authz_session_external_state_s.h" -#include "mongo/db/auth/user_document_parser.h" -#include "mongo/db/auth/user_management_commands_parser.h" #include "mongo/db/auth/user_name.h" #include "mongo/db/jsobj.h" -#include "mongo/db/operation_context.h" #include "mongo/rpc/get_status_from_command_result.h" #include "mongo/s/catalog/sharding_catalog_client.h" #include "mongo/s/grid.h" #include "mongo/stdx/memory.h" #include "mongo/util/mongoutils/str.h" -#include "mongo/util/stringutils.h" namespace mongo { -namespace { - -/** - * Returns the top level field which is expected to be returned by rolesInfo. - */ -std::string rolesFieldName(PrivilegeFormat showPrivileges) { - if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { - return "userFragment"; - } - return "roles"; -} - -/** - * Attches a string representation of a PrivilegeFormat to the provided BSONObjBuilder. - */ -void addShowPrivilegesToBuilder(BSONObjBuilder* builder, PrivilegeFormat showPrivileges) { - if (showPrivileges == PrivilegeFormat::kShowSeparate) { - builder->append("showPrivileges", true); - } else if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) { - builder->append("showPrivileges", "asUserfragment"); - } else { - builder->append("showPrivileges", false); - } -} - -} // namespace - AuthzManagerExternalStateMongos::AuthzManagerExternalStateMongos() = default; AuthzManagerExternalStateMongos::~AuthzManagerExternalStateMongos() = default; @@ -119,111 +88,59 @@ Status AuthzManagerExternalStateMongos::getStoredAuthorizationVersion(OperationC Status AuthzManagerExternalStateMongos::getUserDescription(OperationContext* txn, const UserName& userName, BSONObj* result) { - if (!shouldUseRolesFromConnection(txn, userName)) { - BSONObj usersInfoCmd = - BSON("usersInfo" << BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME - << userName.getUser() - << AuthorizationManager::USER_DB_FIELD_NAME - << userName.getDB())) - << "showPrivileges" - << true - << "showCredentials" - << true); - BSONObjBuilder builder; - const bool ok = grid.catalogClient(txn)->runUserManagementReadCommand( - txn, "admin", usersInfoCmd, &builder); - BSONObj cmdResult = builder.obj(); - if (!ok) { - return getStatusFromCommandResult(cmdResult); - } - - std::vector<BSONElement> foundUsers = cmdResult["users"].Array(); - if (foundUsers.size() == 0) { - return Status(ErrorCodes::UserNotFound, - "User \"" + userName.toString() + "\" not found"); - } - - if (foundUsers.size() > 1) { - return Status(ErrorCodes::UserDataInconsistent, - str::stream() << "Found multiple users on the \"" << userName.getDB() - << "\" database with name \"" - << userName.getUser() - << "\""); - } - *result = foundUsers[0].Obj().getOwned(); - return Status::OK(); - } else { - // Obtain privilege information from the config servers for all roles acquired from the X509 - // certificate. - BSONArrayBuilder userRolesBuilder; - for (const RoleName& role : txn->getClient()->session()->getX509PeerInfo().roles) { - userRolesBuilder.append(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME - << role.getRole() - << AuthorizationManager::ROLE_DB_FIELD_NAME - << role.getDB())); - } - BSONArray providedRoles = userRolesBuilder.arr(); - - BSONObj rolesInfoCmd = BSON("rolesInfo" << providedRoles << "showPrivileges" - << "asUserFragment"); - - BSONObjBuilder cmdResultBuilder; - const bool cmdOk = grid.catalogClient(txn)->runUserManagementReadCommand( - txn, "admin", rolesInfoCmd, &cmdResultBuilder); - BSONObj cmdResult = cmdResultBuilder.obj(); - if (!cmdOk || !cmdResult["userFragment"].ok()) { - return Status(ErrorCodes::FailedToParse, - "Unable to get resolved X509 roles from config server: " + - getStatusFromCommandResult(cmdResult).toString()); - } - cmdResult = cmdResult["userFragment"].Obj().getOwned(); - BSONElement userRoles = cmdResult["roles"]; - BSONElement userInheritedRoles = cmdResult["inheritedRoles"]; - BSONElement userInheritedPrivileges = cmdResult["inheritedPrivileges"]; + BSONObj usersInfoCmd = + BSON("usersInfo" << BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME + << userName.getUser() + << AuthorizationManager::USER_DB_FIELD_NAME + << userName.getDB())) + << "showPrivileges" + << true + << "showCredentials" + << true); + BSONObjBuilder builder; + const bool ok = + grid.catalogClient(txn)->runUserManagementReadCommand(txn, "admin", usersInfoCmd, &builder); + BSONObj cmdResult = builder.obj(); + if (!ok) { + return getStatusFromCommandResult(cmdResult); + } - if (userRoles.eoo() || userInheritedRoles.eoo() || userInheritedPrivileges.eoo() || - !userRoles.isABSONObj() || !userInheritedRoles.isABSONObj() || - !userInheritedPrivileges.isABSONObj()) { - return Status( - ErrorCodes::UserDataInconsistent, - "Recieved malformed response to request for X509 roles from config server"); - } + std::vector<BSONElement> foundUsers = cmdResult["users"].Array(); + if (foundUsers.size() == 0) { + return Status(ErrorCodes::UserNotFound, "User \"" + userName.toString() + "\" not found"); + } - *result = BSON("_id" << userName.getUser() << "user" << userName.getUser() << "db" - << userName.getDB() - << "credentials" - << BSON("external" << true) - << "roles" - << BSONArray(cmdResult["roles"].Obj()) - << "inheritedRoles" - << BSONArray(cmdResult["inheritedRoles"].Obj()) - << "inheritedPrivileges" - << BSONArray(cmdResult["inheritedPrivileges"].Obj())); - return Status::OK(); + if (foundUsers.size() > 1) { + return Status(ErrorCodes::UserDataInconsistent, + str::stream() << "Found multiple users on the \"" << userName.getDB() + << "\" database with name \"" + << userName.getUser() + << "\""); } + *result = foundUsers[0].Obj().getOwned(); + return Status::OK(); } Status AuthzManagerExternalStateMongos::getRoleDescription(OperationContext* txn, const RoleName& roleName, - PrivilegeFormat showPrivileges, + bool showPrivileges, BSONObj* result) { - BSONObjBuilder rolesInfoCmd; - rolesInfoCmd.append("rolesInfo", - BSON_ARRAY(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME - << roleName.getRole() - << AuthorizationManager::ROLE_DB_FIELD_NAME - << roleName.getDB()))); - addShowPrivilegesToBuilder(&rolesInfoCmd, showPrivileges); - + BSONObj rolesInfoCmd = + BSON("rolesInfo" << BSON_ARRAY(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME + << roleName.getRole() + << AuthorizationManager::ROLE_DB_FIELD_NAME + << roleName.getDB())) + << "showPrivileges" + << showPrivileges); BSONObjBuilder builder; - const bool ok = grid.catalogClient(txn)->runUserManagementReadCommand( - txn, "admin", rolesInfoCmd.obj(), &builder); + const bool ok = + grid.catalogClient(txn)->runUserManagementReadCommand(txn, "admin", rolesInfoCmd, &builder); BSONObj cmdResult = builder.obj(); if (!ok) { return getStatusFromCommandResult(cmdResult); } - std::vector<BSONElement> foundRoles = cmdResult[rolesFieldName(showPrivileges)].Array(); + std::vector<BSONElement> foundRoles = cmdResult["roles"].Array(); if (foundRoles.size() == 0) { return Status(ErrorCodes::RoleNotFound, "Role \"" + roleName.toString() + "\" not found"); } @@ -238,59 +155,23 @@ Status AuthzManagerExternalStateMongos::getRoleDescription(OperationContext* txn *result = foundRoles[0].Obj().getOwned(); return Status::OK(); } -Status AuthzManagerExternalStateMongos::getRolesDescription(OperationContext* txn, - const std::vector<RoleName>& roles, - PrivilegeFormat showPrivileges, - BSONObj* result) { - BSONArrayBuilder rolesInfoCmdArray; - - for (const RoleName& roleName : roles) { - rolesInfoCmdArray << BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME - << roleName.getRole() - << AuthorizationManager::ROLE_DB_FIELD_NAME - << roleName.getDB()); - } - - BSONObjBuilder rolesInfoCmd; - rolesInfoCmd.append("rolesInfo", rolesInfoCmdArray.arr()); - addShowPrivilegesToBuilder(&rolesInfoCmd, showPrivileges); - BSONObjBuilder builder; - const bool ok = grid.catalogClient(txn)->runUserManagementReadCommand( - txn, "admin", rolesInfoCmd.obj(), &builder); - BSONObj cmdResult = builder.obj(); - if (!ok) { - return getStatusFromCommandResult(cmdResult); - } - - std::vector<BSONElement> foundRoles = cmdResult[rolesFieldName(showPrivileges)].Array(); - if (foundRoles.size() == 0) { - return Status(ErrorCodes::RoleNotFound, "Roles not found"); - } - - *result = foundRoles[0].Obj().getOwned(); - - return Status::OK(); -} Status AuthzManagerExternalStateMongos::getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - PrivilegeFormat showPrivileges, + bool showPrivileges, bool showBuiltinRoles, std::vector<BSONObj>* result) { - BSONObjBuilder rolesInfoCmd; - rolesInfoCmd << "rolesInfo" << 1 << "showBuiltinRoles" << showBuiltinRoles; - addShowPrivilegesToBuilder(&rolesInfoCmd, showPrivileges); - + BSONObj rolesInfoCmd = + BSON("rolesInfo" << 1 << "showPrivileges" << showPrivileges << "showBuiltinRoles" + << showBuiltinRoles); BSONObjBuilder builder; - const bool ok = grid.catalogClient(txn)->runUserManagementReadCommand( - txn, dbname, rolesInfoCmd.obj(), &builder); + const bool ok = + grid.catalogClient(txn)->runUserManagementReadCommand(txn, dbname, rolesInfoCmd, &builder); BSONObj cmdResult = builder.obj(); if (!ok) { return getStatusFromCommandResult(cmdResult); } - - for (BSONObjIterator it(cmdResult[rolesFieldName(showPrivileges)].Obj()); it.more(); - it.next()) { + for (BSONObjIterator it(cmdResult["roles"].Obj()); it.more(); it.next()) { result->push_back((*it).Obj().getOwned()); } return Status::OK(); diff --git a/src/mongo/db/auth/authz_manager_external_state_s.h b/src/mongo/db/auth/authz_manager_external_state_s.h index 9f9c5c12d2b..bf9ca876c43 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.h +++ b/src/mongo/db/auth/authz_manager_external_state_s.h @@ -59,15 +59,11 @@ public: BSONObj* result); virtual Status getRoleDescription(OperationContext* txn, const RoleName& roleName, - PrivilegeFormat showPrivileges, + bool showPrivileges, BSONObj* result); - virtual Status getRolesDescription(OperationContext* txn, - const std::vector<RoleName>& roles, - PrivilegeFormat showPrivileges, - BSONObj* result); virtual Status getRoleDescriptionsForDB(OperationContext* txn, const std::string dbname, - PrivilegeFormat showPrivileges, + bool showPrivileges, bool showBuiltinRoles, std::vector<BSONObj>* result); diff --git a/src/mongo/db/auth/privilege_format.h b/src/mongo/db/auth/privilege_format.h deleted file mode 100644 index 2f452a3ae84..00000000000 --- a/src/mongo/db/auth/privilege_format.h +++ /dev/null @@ -1,42 +0,0 @@ -/** -* 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 - - -namespace mongo { -/** - * How user management functions should structure the BSON representation of privileges and roles. - */ -enum class PrivilegeFormat { - kOmit, // Privileges should not be included in the BSON representation. - kShowSeparate, // Privileges should be included, each as a separate entry. - kShowAsUserFragment // Privileges and roles should all be collapsed together, and presented as - // a fragment of a user document. -}; -} // namespace mongo diff --git a/src/mongo/db/auth/sasl_authentication_session.cpp b/src/mongo/db/auth/sasl_authentication_session.cpp index fdb7f024929..c64e4be8100 100644 --- a/src/mongo/db/auth/sasl_authentication_session.cpp +++ b/src/mongo/db/auth/sasl_authentication_session.cpp @@ -69,7 +69,6 @@ bool isAuthorizedCommon(SaslAuthenticationSession* session, SaslAuthenticationSession::SaslAuthenticationSession(AuthorizationSession* authzSession) : AuthenticationSession(AuthenticationSession::SESSION_TYPE_SASL), - _txn(nullptr), _authzSession(authzSession), _saslStep(0), _conversationId(0), diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp index aee95bbbdef..090ca7e0246 100644 --- a/src/mongo/db/auth/user_management_commands_parser.cpp +++ b/src/mongo/db/auth/user_management_commands_parser.cpp @@ -414,20 +414,10 @@ Status parseRolesInfoCommand(const BSONObj& cmdObj, StringData dbname, RolesInfo parsedArgs->roleNames.push_back(name); } - BSONElement showPrivileges = cmdObj["showPrivileges"]; - if (showPrivileges.eoo()) { - parsedArgs->privilegeFormat = PrivilegeFormat::kOmit; - } else if (showPrivileges.isNumber() || showPrivileges.isBoolean()) { - parsedArgs->privilegeFormat = - showPrivileges.trueValue() ? PrivilegeFormat::kShowSeparate : PrivilegeFormat::kOmit; - } else if (showPrivileges.type() == BSONType::String && - showPrivileges.String() == "asUserFragment") { - parsedArgs->privilegeFormat = PrivilegeFormat::kShowAsUserFragment; - } else { - return Status(ErrorCodes::FailedToParse, - str::stream() << "Failed to parse 'showPrivileges'. 'showPrivileges' should " - "either be a boolean or the string 'asUserFragment', given: " - << showPrivileges.toString()); + status = bsonExtractBooleanFieldWithDefault( + cmdObj, "showPrivileges", false, &parsedArgs->showPrivileges); + if (!status.isOK()) { + return status; } status = bsonExtractBooleanFieldWithDefault( diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h index 720c4c1d9d7..94dc3b7b2ae 100644 --- a/src/mongo/db/auth/user_management_commands_parser.h +++ b/src/mongo/db/auth/user_management_commands_parser.h @@ -35,7 +35,6 @@ #include "mongo/base/status.h" #include "mongo/base/string_data.h" #include "mongo/db/auth/privilege.h" -#include "mongo/db/auth/privilege_format.h" #include "mongo/db/auth/role_name.h" #include "mongo/db/auth/user.h" #include "mongo/db/auth/user_name.h" @@ -115,10 +114,9 @@ Status parseUsersInfoCommand(const BSONObj& cmdObj, StringData dbname, UsersInfo struct RolesInfoArgs { std::vector<RoleName> roleNames; bool allForDB; - PrivilegeFormat privilegeFormat; + bool showPrivileges; bool showBuiltinRoles; - RolesInfoArgs() - : allForDB(false), privilegeFormat(PrivilegeFormat::kOmit), showBuiltinRoles(false) {} + RolesInfoArgs() : allForDB(false), showPrivileges(false), showBuiltinRoles(false) {} }; /** diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp index a6a9a6f4508..e5b50fec80d 100644 --- a/src/mongo/db/commands/authentication_commands.cpp +++ b/src/mongo/db/commands/authentication_commands.cpp @@ -314,7 +314,7 @@ Status CmdAuthenticate::_authenticateX509(OperationContext* txn, Client* client = Client::getCurrent(); AuthorizationSession* authorizationSession = AuthorizationSession::get(client); - auto clientName = client->session()->getX509PeerInfo().subjectName; + auto clientName = client->session()->getX509SubjectName(); if (!getSSLManager()->getSSLConfiguration().hasCA) { return Status(ErrorCodes::AuthenticationFailed, diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index 2238383d960..5dc5bba8138 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -180,8 +180,7 @@ Status checkOkayToGrantRolesToRole(OperationContext* txn, } BSONObj roleToAddDoc; - Status status = - authzManager->getRoleDescription(txn, roleToAdd, PrivilegeFormat::kOmit, &roleToAddDoc); + Status status = authzManager->getRoleDescription(txn, roleToAdd, false, &roleToAddDoc); if (status == ErrorCodes::RoleNotFound) { return Status(ErrorCodes::RoleNotFound, "Cannot grant nonexistent role " + roleToAdd.toString()); @@ -716,8 +715,7 @@ public: // Role existence has to be checked after acquiring the update lock for (size_t i = 0; i < args.roles.size(); ++i) { BSONObj ignored; - status = authzManager->getRoleDescription( - txn, args.roles[i], PrivilegeFormat::kOmit, &ignored); + status = authzManager->getRoleDescription(txn, args.roles[i], false, &ignored); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -829,8 +827,7 @@ public: if (args.hasRoles) { for (size_t i = 0; i < args.roles.size(); ++i) { BSONObj ignored; - status = authzManager->getRoleDescription( - txn, args.roles[i], PrivilegeFormat::kOmit, &ignored); + status = authzManager->getRoleDescription(txn, args.roles[i], false, &ignored); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1038,8 +1035,7 @@ public: for (vector<RoleName>::iterator it = roles.begin(); it != roles.end(); ++it) { RoleName& roleName = *it; BSONObj roleDoc; - status = - authzManager->getRoleDescription(txn, roleName, PrivilegeFormat::kOmit, &roleDoc); + status = authzManager->getRoleDescription(txn, roleName, false, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1113,8 +1109,7 @@ public: for (vector<RoleName>::iterator it = roles.begin(); it != roles.end(); ++it) { RoleName& roleName = *it; BSONObj roleDoc; - status = - authzManager->getRoleDescription(txn, roleName, PrivilegeFormat::kOmit, &roleDoc); + status = authzManager->getRoleDescription(txn, roleName, false, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1429,8 +1424,7 @@ public: // Role existence has to be checked after acquiring the update lock BSONObj ignored; - status = - authzManager->getRoleDescription(txn, args.roleName, PrivilegeFormat::kOmit, &ignored); + status = authzManager->getRoleDescription(txn, args.roleName, false, &ignored); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1489,7 +1483,6 @@ public: int options, string& errmsg, BSONObjBuilder& result) { - RoleName roleName; PrivilegeVector privilegesToAdd; Status status = auth::parseAndValidateRolePrivilegeManipulationCommands( @@ -1521,8 +1514,7 @@ public: } BSONObj roleDoc; - status = authzManager->getRoleDescription( - txn, roleName, PrivilegeFormat::kShowSeparate, &roleDoc); + status = authzManager->getRoleDescription(txn, roleName, true, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1624,8 +1616,7 @@ public: } BSONObj roleDoc; - status = authzManager->getRoleDescription( - txn, roleName, PrivilegeFormat::kShowSeparate, &roleDoc); + status = authzManager->getRoleDescription(txn, roleName, true, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1737,7 +1728,7 @@ public: // Role existence has to be checked after acquiring the update lock BSONObj roleDoc; - status = authzManager->getRoleDescription(txn, roleName, PrivilegeFormat::kOmit, &roleDoc); + status = authzManager->getRoleDescription(txn, roleName, false, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1827,7 +1818,7 @@ public: } BSONObj roleDoc; - status = authzManager->getRoleDescription(txn, roleName, PrivilegeFormat::kOmit, &roleDoc); + status = authzManager->getRoleDescription(txn, roleName, false, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -1914,7 +1905,7 @@ public: } BSONObj roleDoc; - status = authzManager->getRoleDescription(txn, roleName, PrivilegeFormat::kOmit, &roleDoc); + status = authzManager->getRoleDescription(txn, roleName, false, &roleDoc); if (!status.isOK()) { return appendCommandStatus(result, status); } @@ -2133,29 +2124,6 @@ public: } cmdDropAllRolesFromDatabase; -/** - * Provides information about one or more roles, the indirect roles they are members of, and - * optionally the privileges they provide. - * - * This command accepts the following arguments: - * rolesInfo: - * (String) Returns information about a single role on the current database. - * {role: (String), db: (String)} Returns information about a specified role, on a specific db - * (BooleanTrue) Returns information about all roles in this database - * [ //Zero or more of - * {role: (String), db: (String) ] Returns information about all specified roles - * - * showBuiltinRoles: - * (Boolean) If true, and rolesInfo == (BooleanTrue), include built-in roles from the database - * - * showPrivileges: - * (BooleanFalse) Do not show information about privileges - * (BooleanTrue) Attach all privileges inherited from roles to role descriptions - * "asUserFragment" Render results as a partial user document as-if a user existed which possessed - * these roles. This format may change over time with changes to the auth - * schema. - */ - class CmdRolesInfo : public Command { public: virtual bool slaveOk() const { @@ -2199,40 +2167,33 @@ public: return appendCommandStatus(result, status); } + BSONArrayBuilder rolesArrayBuilder; if (args.allForDB) { std::vector<BSONObj> rolesDocs; status = getGlobalAuthorizationManager()->getRoleDescriptionsForDB( - txn, dbname, args.privilegeFormat, args.showBuiltinRoles, &rolesDocs); + txn, dbname, args.showPrivileges, args.showBuiltinRoles, &rolesDocs); if (!status.isOK()) { return appendCommandStatus(result, status); } - if (args.privilegeFormat == PrivilegeFormat::kShowAsUserFragment) { - return appendCommandStatus( - result, - Status(ErrorCodes::IllegalOperation, - "Cannot get user fragment for all roles in a database")); - } - BSONArrayBuilder rolesArrayBuilder; for (size_t i = 0; i < rolesDocs.size(); ++i) { rolesArrayBuilder.append(rolesDocs[i]); } - result.append("roles", rolesArrayBuilder.arr()); - } - - BSONObj roleDetails; - status = getGlobalAuthorizationManager()->getRolesDescription( - txn, args.roleNames, args.privilegeFormat, &roleDetails); - if (!status.isOK()) { - return appendCommandStatus(result, status); - } - - if (args.privilegeFormat == PrivilegeFormat::kShowAsUserFragment) { - result.append("userFragment", roleDetails); } else { - result.append("roles", BSONArray(roleDetails)); + for (size_t i = 0; i < args.roleNames.size(); ++i) { + BSONObj roleDetails; + status = getGlobalAuthorizationManager()->getRoleDescription( + txn, args.roleNames[i], args.showPrivileges, &roleDetails); + if (status.code() == ErrorCodes::RoleNotFound) { + continue; + } + if (!status.isOK()) { + return appendCommandStatus(result, status); + } + rolesArrayBuilder.append(roleDetails); + } } - + result.append("roles", rolesArrayBuilder.arr()); return true; } diff --git a/src/mongo/transport/service_entry_point_test_suite.cpp b/src/mongo/transport/service_entry_point_test_suite.cpp index c3814accc47..841851d5ccd 100644 --- a/src/mongo/transport/service_entry_point_test_suite.cpp +++ b/src/mongo/transport/service_entry_point_test_suite.cpp @@ -51,7 +51,6 @@ #include "mongo/transport/transport_layer.h" #include "mongo/unittest/unittest.h" #include "mongo/util/net/message.h" -#include "mongo/util/net/ssl_types.h" namespace mongo { @@ -149,9 +148,8 @@ void ServiceEntryPointTestSuite::MockTLHarness::asyncWait(Ticket&& ticket, return _asyncWait(std::move(ticket), std::move(callback)); } -SSLPeerInfo ServiceEntryPointTestSuite::MockTLHarness::getX509PeerInfo( - const Session& session) const { - return SSLPeerInfo("mock", {}); +std::string ServiceEntryPointTestSuite::MockTLHarness::getX509SubjectName(const Session& session) { + return "mock"; } void ServiceEntryPointTestSuite::MockTLHarness::registerTags(const Session& session) {} diff --git a/src/mongo/transport/service_entry_point_test_suite.h b/src/mongo/transport/service_entry_point_test_suite.h index 7f15d4723eb..2249c86b9bd 100644 --- a/src/mongo/transport/service_entry_point_test_suite.h +++ b/src/mongo/transport/service_entry_point_test_suite.h @@ -39,7 +39,6 @@ namespace mongo { class ServiceEntryPoint; -struct SSLPeerInfo; /** * Test class. Uses a mock TransportLayer to test that the ServiceEntryPoint @@ -130,7 +129,7 @@ private: Date_t expiration = transport::Ticket::kNoExpirationDate) override; Status wait(transport::Ticket&& ticket) override; void asyncWait(transport::Ticket&& ticket, TicketCallback callback) override; - SSLPeerInfo getX509PeerInfo(const transport::Session& session) const override; + std::string getX509SubjectName(const transport::Session& session) override; void registerTags(const transport::Session& session) override; Stats sessionStats() override; void end(transport::Session& session) override; diff --git a/src/mongo/transport/session.cpp b/src/mongo/transport/session.cpp index 851f348edc0..b341cd97630 100644 --- a/src/mongo/transport/session.cpp +++ b/src/mongo/transport/session.cpp @@ -32,7 +32,6 @@ #include "mongo/platform/atomic_word.h" #include "mongo/transport/transport_layer.h" -#include "mongo/util/net/ssl_types.h" namespace mongo { namespace transport { @@ -96,8 +95,8 @@ Ticket Session::sinkMessage(const Message& message, Date_t expiration) { return _tl->sinkMessage(*this, message, expiration); } -SSLPeerInfo Session::getX509PeerInfo() const { - return _tl->getX509PeerInfo(*this); +std::string Session::getX509SubjectName() const { + return _tl->getX509SubjectName(*this); } void Session::end() { diff --git a/src/mongo/transport/session.h b/src/mongo/transport/session.h index 76c2a975478..c7ec5cd28f0 100644 --- a/src/mongo/transport/session.h +++ b/src/mongo/transport/session.h @@ -37,9 +37,6 @@ #include "mongo/util/time_support.h" namespace mongo { - -struct SSLPeerInfo; - namespace transport { class TransportLayer; @@ -105,9 +102,9 @@ public: } /** - * Return the X509 peer information for this connection (SSL only). + * Return the X509 subject name for this connection (SSL only). */ - SSLPeerInfo getX509PeerInfo() const; + std::string getX509SubjectName() const; /** * Set this session's tags. This Session will register diff --git a/src/mongo/transport/transport_layer.h b/src/mongo/transport/transport_layer.h index c407eef1f0d..3634ec68de5 100644 --- a/src/mongo/transport/transport_layer.h +++ b/src/mongo/transport/transport_layer.h @@ -154,10 +154,10 @@ public: virtual void registerTags(const Session& session) = 0; /** - * Return the stored X509 peer information for this session. If the session does not - * exist in this TransportLayer, returns a default constructed object. + * Return the stored X509 subject name for this session. If the session does not + * exist in this TransportLayer, returns "". */ - virtual SSLPeerInfo getX509PeerInfo(const Session& session) const = 0; + virtual std::string getX509SubjectName(const Session& session) = 0; /** * Returns the number of sessions currently open in the transport layer. diff --git a/src/mongo/transport/transport_layer_legacy.cpp b/src/mongo/transport/transport_layer_legacy.cpp index 1a719d66e93..3c004eb793f 100644 --- a/src/mongo/transport/transport_layer_legacy.cpp +++ b/src/mongo/transport/transport_layer_legacy.cpp @@ -117,16 +117,16 @@ Ticket TransportLayerLegacy::sourceMessage(Session& session, Message* message, D return Ticket(this, stdx::make_unique<LegacyTicket>(session, expiration, std::move(sourceCb))); } -SSLPeerInfo TransportLayerLegacy::getX509PeerInfo(const Session& session) const { +std::string TransportLayerLegacy::getX509SubjectName(const Session& session) { { stdx::lock_guard<stdx::mutex> lk(_connectionsMutex); auto conn = _connections.find(session.id()); if (conn == _connections.end()) { // Return empty string if the session is not found - return SSLPeerInfo(); + return ""; } - return conn->second.sslPeerInfo.value_or(SSLPeerInfo()); + return conn->second.x509SubjectName.value_or(""); } } @@ -274,10 +274,10 @@ Status TransportLayerLegacy::_runTicket(Ticket ticket) { #ifdef MONGO_CONFIG_SSL // If we didn't have an X509 subject name, see if we have one now - if (!conn->second.sslPeerInfo) { - auto info = amp->getX509PeerInfo(); - if (info.subjectName != "") { - conn->second.sslPeerInfo = info; + if (!conn->second.x509SubjectName) { + auto name = amp->getX509SubjectName(); + if (name != "") { + conn->second.x509SubjectName = name; } } #endif diff --git a/src/mongo/transport/transport_layer_legacy.h b/src/mongo/transport/transport_layer_legacy.h index 873da5a2123..7472212ffc9 100644 --- a/src/mongo/transport/transport_layer_legacy.h +++ b/src/mongo/transport/transport_layer_legacy.h @@ -79,7 +79,7 @@ public: void asyncWait(Ticket&& ticket, TicketCallback callback) override; void registerTags(const Session& session) override; - SSLPeerInfo getX509PeerInfo(const Session& session) const override; + std::string getX509SubjectName(const Session& session) override; Stats sessionStats() override; @@ -150,7 +150,7 @@ private: const long long connectionId; - boost::optional<SSLPeerInfo> sslPeerInfo; + boost::optional<std::string> x509SubjectName; Session::TagMask tags; bool inUse; bool ended; @@ -161,7 +161,7 @@ private: std::unique_ptr<Listener> _listener; stdx::thread _listenerThread; - mutable stdx::mutex _connectionsMutex; + stdx::mutex _connectionsMutex; std::unordered_map<Session::Id, Connection> _connections; void _endSession_inlock(decltype(_connections.begin()) conn); diff --git a/src/mongo/transport/transport_layer_manager.cpp b/src/mongo/transport/transport_layer_manager.cpp index 6fc191bf49f..e513155e5cd 100644 --- a/src/mongo/transport/transport_layer_manager.cpp +++ b/src/mongo/transport/transport_layer_manager.cpp @@ -33,7 +33,6 @@ #include "mongo/base/status.h" #include "mongo/stdx/memory.h" #include "mongo/transport/session.h" -#include "mongo/util/net/ssl_types.h" #include "mongo/util/time_support.h" #include <limits> @@ -62,8 +61,8 @@ void TransportLayerManager::asyncWait(Ticket&& ticket, TicketCallback callback) return getTicketTransportLayer(ticket)->asyncWait(std::move(ticket), std::move(callback)); } -SSLPeerInfo TransportLayerManager::getX509PeerInfo(const Session& session) const { - return session.getX509PeerInfo(); +std::string TransportLayerManager::getX509SubjectName(const Session& session) { + return session.getX509SubjectName(); } template <typename Callable> diff --git a/src/mongo/transport/transport_layer_manager.h b/src/mongo/transport/transport_layer_manager.h index 9648a0859f2..20d27d6571c 100644 --- a/src/mongo/transport/transport_layer_manager.h +++ b/src/mongo/transport/transport_layer_manager.h @@ -64,7 +64,7 @@ public: Status wait(Ticket&& ticket) override; void asyncWait(Ticket&& ticket, TicketCallback callback) override; - SSLPeerInfo getX509PeerInfo(const Session& session) const override; + std::string getX509SubjectName(const Session& session) override; void registerTags(const Session& session) override; Stats sessionStats() override; diff --git a/src/mongo/transport/transport_layer_mock.cpp b/src/mongo/transport/transport_layer_mock.cpp index 5b66c00bf91..e7fa76d2e9b 100644 --- a/src/mongo/transport/transport_layer_mock.cpp +++ b/src/mongo/transport/transport_layer_mock.cpp @@ -109,13 +109,8 @@ void TransportLayerMock::asyncWait(Ticket&& ticket, TicketCallback callback) { callback(Status::OK()); } -SSLPeerInfo TransportLayerMock::getX509PeerInfo(const Session& session) const { - return _sessions.at(session.id()).peerInfo; -} - - -void TransportLayerMock::setX509PeerInfo(const Session& session, SSLPeerInfo peerInfo) { - _sessions[session.id()].peerInfo = std::move(peerInfo); +std::string TransportLayerMock::getX509SubjectName(const Session& session) { + return session.getX509SubjectName(); } TransportLayer::Stats TransportLayerMock::sessionStats() { @@ -129,16 +124,16 @@ Session* TransportLayerMock::createSession() { stdx::make_unique<Session>(HostAndPort(), HostAndPort(), this); Session::Id sessionId = session->id(); - _sessions[sessionId] = Connection{std::move(session), SSLPeerInfo()}; + _sessions[sessionId] = std::move(session); - return _sessions[sessionId].session.get(); + return _sessions[sessionId].get(); } Session* TransportLayerMock::get(Session::Id id) { if (!owns(id)) return nullptr; - return _sessions[id].session.get(); + return _sessions[id].get(); } bool TransportLayerMock::owns(Session::Id id) { @@ -152,7 +147,7 @@ void TransportLayerMock::end(Session& session) { void TransportLayerMock::endAllSessions(Session::TagMask tags) { auto it = _sessions.begin(); while (it != _sessions.end()) { - end(*it->second.session.get()); + end(*it->second.get()); it++; } } diff --git a/src/mongo/transport/transport_layer_mock.h b/src/mongo/transport/transport_layer_mock.h index 10913f0cf96..38ab3eed0f1 100644 --- a/src/mongo/transport/transport_layer_mock.h +++ b/src/mongo/transport/transport_layer_mock.h @@ -36,7 +36,6 @@ #include "mongo/transport/ticket_impl.h" #include "mongo/transport/transport_layer.h" #include "mongo/util/net/message.h" -#include "mongo/util/net/ssl_types.h" #include "mongo/util/time_support.h" namespace mongo { @@ -87,8 +86,7 @@ public: Status wait(Ticket&& ticket) override; void asyncWait(Ticket&& ticket, TicketCallback callback) override; - SSLPeerInfo getX509PeerInfo(const Session& session) const override; - void setX509PeerInfo(const Session& session, SSLPeerInfo peerInfo); + std::string getX509SubjectName(const Session& session) override; void registerTags(const Session& session) override; Stats sessionStats() override; @@ -104,11 +102,7 @@ public: bool inShutdown() const; private: - struct Connection { - std::unique_ptr<Session> session; - SSLPeerInfo peerInfo; - }; - std::unordered_map<Session::Id, Connection> _sessions; + std::unordered_map<Session::Id, std::unique_ptr<Session>> _sessions; bool _shutdown; }; diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript index c9da1751462..34105547ab6 100644 --- a/src/mongo/util/net/SConscript +++ b/src/mongo/util/net/SConscript @@ -49,7 +49,6 @@ networkEnv.Library( "thread_idle_callback.cpp", ], LIBDEPS=[ - '$BUILD_DIR/mongo/db/auth/auth_rolename', '$BUILD_DIR/mongo/db/server_options_core', '$BUILD_DIR/mongo/db/server_parameters', '$BUILD_DIR/mongo/util/background_job', diff --git a/src/mongo/util/net/abstract_message_port.h b/src/mongo/util/net/abstract_message_port.h index 99ffce63977..10860552a7d 100644 --- a/src/mongo/util/net/abstract_message_port.h +++ b/src/mongo/util/net/abstract_message_port.h @@ -35,7 +35,6 @@ #include "mongo/stdx/functional.h" #include "mongo/util/net/message.h" #include "mongo/util/net/sockaddr.h" -#include "mongo/util/net/ssl_types.h" #include "mongo/util/time_support.h" namespace mongo { @@ -156,14 +155,14 @@ public: virtual long long getBytesOut() const = 0; /** - * Set the x509 peer information (used for SSL). + * Set the x509 subject name (used for SSL). */ - virtual void setX509PeerInfo(SSLPeerInfo x509PeerInfo) = 0; + virtual void setX509SubjectName(const std::string& x509SubjectName) = 0; /** - * Get the current x509 peer information (used for SSL). + * Get the current x509 subject name (used for SSL). */ - virtual const SSLPeerInfo& getX509PeerInfo() const = 0; + virtual std::string getX509SubjectName() const = 0; /** * Set the connection ID. diff --git a/src/mongo/util/net/asio_message_port.cpp b/src/mongo/util/net/asio_message_port.cpp index d883a474ab0..d5378f8b756 100644 --- a/src/mongo/util/net/asio_message_port.cpp +++ b/src/mongo/util/net/asio_message_port.cpp @@ -83,7 +83,7 @@ ASIOMessagingPort::ASIOMessagingPort(int fd, SockAddr farEnd) _remote(), _isEncrypted(false), _awaitingHandshake(true), - _x509PeerInfo(), + _x509SubjectName(), _bytesIn(0), _bytesOut(0), _logLevel(logger::LogSeverity::Log()), @@ -121,7 +121,7 @@ ASIOMessagingPort::ASIOMessagingPort(Milliseconds timeout, logger::LogSeverity l _remote(), _isEncrypted(false), _awaitingHandshake(true), - _x509PeerInfo(), + _x509SubjectName(), _bytesIn(0), _bytesOut(0), _logLevel(logLevel), @@ -383,13 +383,13 @@ bool ASIOMessagingPort::recv(Message& m) { throw asio::system_error(ec); } - auto swPeerInfo = + auto swPeerSubjectName = getSSLManager()->parseAndValidatePeerCertificate(_sslSock.native_handle(), ""); - if (!swPeerInfo.isOK()) { + if (!swPeerSubjectName.isOK()) { throw SocketException(SocketException::CONNECT_ERROR, - swPeerInfo.getStatus().reason()); + swPeerSubjectName.getStatus().reason()); } - setX509PeerInfo(swPeerInfo.getValue().get_value_or(SSLPeerInfo())); + setX509SubjectName(swPeerSubjectName.getValue().get_value_or("")); _isEncrypted = true; _awaitingHandshake = false; @@ -590,12 +590,13 @@ bool ASIOMessagingPort::secure(SSLManagerInterface* ssl, const std::string& remo return false; } - auto swPeerInfo = + auto swPeerSubjectName = getSSLManager()->parseAndValidatePeerCertificate(_sslSock.native_handle(), remoteHost); - if (!swPeerInfo.isOK()) { - throw SocketException(SocketException::CONNECT_ERROR, swPeerInfo.getStatus().reason()); + if (!swPeerSubjectName.isOK()) { + throw SocketException(SocketException::CONNECT_ERROR, + swPeerSubjectName.getStatus().reason()); } - setX509PeerInfo(swPeerInfo.getValue().get_value_or(SSLPeerInfo())); + setX509SubjectName(swPeerSubjectName.getValue().get_value_or("")); _isEncrypted = true; return true; @@ -629,12 +630,12 @@ long long ASIOMessagingPort::getBytesOut() const { return _bytesOut; } -void ASIOMessagingPort::setX509PeerInfo(SSLPeerInfo x509PeerInfo) { - _x509PeerInfo = std::move(x509PeerInfo); +void ASIOMessagingPort::setX509SubjectName(const std::string& x509SubjectName) { + _x509SubjectName = x509SubjectName; } -const SSLPeerInfo& ASIOMessagingPort::getX509PeerInfo() const { - return _x509PeerInfo; +std::string ASIOMessagingPort::getX509SubjectName() const { + return _x509SubjectName; } void ASIOMessagingPort::setConnectionId(const long long connectionId) { diff --git a/src/mongo/util/net/asio_message_port.h b/src/mongo/util/net/asio_message_port.h index 37280b0b714..d3e5fc33b1c 100644 --- a/src/mongo/util/net/asio_message_port.h +++ b/src/mongo/util/net/asio_message_port.h @@ -105,9 +105,9 @@ public: long long getBytesOut() const override; - void setX509PeerInfo(SSLPeerInfo x509PeerInfo) override; + void setX509SubjectName(const std::string& x509SubjectName) override; - const SSLPeerInfo& getX509PeerInfo() const override; + std::string getX509SubjectName() const override; void setConnectionId(const long long connectionId) override; @@ -140,7 +140,7 @@ private: bool _isEncrypted; bool _awaitingHandshake; - SSLPeerInfo _x509PeerInfo; + std::string _x509SubjectName; long long _bytesIn; long long _bytesOut; diff --git a/src/mongo/util/net/message_port.cpp b/src/mongo/util/net/message_port.cpp index 22031c71e93..137d494c4ac 100644 --- a/src/mongo/util/net/message_port.cpp +++ b/src/mongo/util/net/message_port.cpp @@ -70,7 +70,7 @@ MessagingPort::MessagingPort(double timeout, logger::LogSeverity ll) : MessagingPort(std::make_shared<Socket>(timeout, ll)) {} MessagingPort::MessagingPort(std::shared_ptr<Socket> sock) - : _x509PeerInfo(), _connectionId(), _tag(), _psock(std::move(sock)) { + : _x509SubjectName(), _connectionId(), _tag(), _psock(std::move(sock)) { SockAddr sa = _psock->remoteAddr(); _remoteParsed = HostAndPort(sa.getAddr(), sa.getPort()); } @@ -127,10 +127,9 @@ bool MessagingPort::recv(Message& m) { uassert(17132, "SSL handshake received but server is started without SSL support", sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled); - setX509PeerInfo( + setX509SubjectName( _psock->doSSLHandshake(reinterpret_cast<const char*>(&header), sizeof(header))); _psock->setHandshakeReceived(); - goto again; } uassert(17189, @@ -217,12 +216,12 @@ SockAddr MessagingPort::localAddr() const { return _psock->localAddr(); } -void MessagingPort::setX509PeerInfo(SSLPeerInfo x509PeerInfo) { - _x509PeerInfo = std::move(x509PeerInfo); +void MessagingPort::setX509SubjectName(const std::string& x509SubjectName) { + _x509SubjectName = x509SubjectName; } -const SSLPeerInfo& MessagingPort::getX509PeerInfo() const { - return _x509PeerInfo; +std::string MessagingPort::getX509SubjectName() const { + return _x509SubjectName; } void MessagingPort::setConnectionId(const long long connectionId) { diff --git a/src/mongo/util/net/message_port.h b/src/mongo/util/net/message_port.h index 6163e84fe3c..41ecc2fce3b 100644 --- a/src/mongo/util/net/message_port.h +++ b/src/mongo/util/net/message_port.h @@ -101,9 +101,9 @@ public: return _psock->getBytesOut(); } - void setX509PeerInfo(SSLPeerInfo x509PeerInfo) override; + void setX509SubjectName(const std::string& x509SubjectName) override; - const SSLPeerInfo& getX509PeerInfo() const override; + std::string getX509SubjectName() const override; void setConnectionId(const long long connectionId) override; @@ -139,7 +139,7 @@ public: private: // this is the parsed version of remote HostAndPort _remoteParsed; - SSLPeerInfo _x509PeerInfo; + std::string _x509SubjectName; long long _connectionId; AbstractMessagingPort::Tag _tag; std::shared_ptr<Socket> _psock; diff --git a/src/mongo/util/net/message_port_mock.cpp b/src/mongo/util/net/message_port_mock.cpp index c09d69f2bba..de07ba032ae 100644 --- a/src/mongo/util/net/message_port_mock.cpp +++ b/src/mongo/util/net/message_port_mock.cpp @@ -36,7 +36,7 @@ namespace mongo { using std::string; -MessagingPortMock::MessagingPortMock() : AbstractMessagingPort(), _x509PeerInfo() {} +MessagingPortMock::MessagingPortMock() : AbstractMessagingPort() {} MessagingPortMock::~MessagingPortMock() {} void MessagingPortMock::setTimeout(Milliseconds millis) {} @@ -101,10 +101,10 @@ uint64_t MessagingPortMock::getSockCreationMicroSec() const { return 0; } -void MessagingPortMock::setX509PeerInfo(SSLPeerInfo x509PeerInfo) {} +void MessagingPortMock::setX509SubjectName(const std::string& x509SubjectName) {} -const SSLPeerInfo& MessagingPortMock::getX509PeerInfo() const { - return _x509PeerInfo; +std::string MessagingPortMock::getX509SubjectName() const { + return "mock"; } void MessagingPortMock::setConnectionId(const long long connectionId) {} diff --git a/src/mongo/util/net/message_port_mock.h b/src/mongo/util/net/message_port_mock.h index bb7e8b678d7..721998f81f3 100644 --- a/src/mongo/util/net/message_port_mock.h +++ b/src/mongo/util/net/message_port_mock.h @@ -79,9 +79,9 @@ public: uint64_t getSockCreationMicroSec() const override; - void setX509PeerInfo(SSLPeerInfo x509PeerInfo) override; + void setX509SubjectName(const std::string& x509SubjectName) override; - const SSLPeerInfo& getX509PeerInfo() const override; + std::string getX509SubjectName() const override; void setConnectionId(const long long connectionId) override; @@ -97,7 +97,6 @@ public: private: HostAndPort _remote; - SSLPeerInfo _x509PeerInfo; }; } // namespace mongo diff --git a/src/mongo/util/net/sock.cpp b/src/mongo/util/net/sock.cpp index ed4092bbac1..5466c21dd4b 100644 --- a/src/mongo/util/net/sock.cpp +++ b/src/mongo/util/net/sock.cpp @@ -306,9 +306,9 @@ void Socket::secureAccepted(SSLManagerInterface* ssl) { _sslManager = ssl; } -SSLPeerInfo Socket::doSSLHandshake(const char* firstBytes, int len) { +std::string Socket::doSSLHandshake(const char* firstBytes, int len) { if (!_sslManager) - return SSLPeerInfo(); + return ""; fassert(16506, _fd != INVALID_SOCKET); if (_sslConnection.get()) { throw SocketException(SocketException::RECV_ERROR, diff --git a/src/mongo/util/net/sock.h b/src/mongo/util/net/sock.h index 9828ddcf1f5..a9da4bdbbfb 100644 --- a/src/mongo/util/net/sock.h +++ b/src/mongo/util/net/sock.h @@ -63,7 +63,6 @@ namespace mongo { class SSLManagerInterface; class SSLConnection; #endif -struct SSLPeerInfo; extern const int portSendFlags; extern const int portRecvFlags; @@ -222,7 +221,7 @@ public: * * This function may throw SocketException. */ - SSLPeerInfo doSSLHandshake(const char* firstBytes = NULL, int len = 0); + std::string doSSLHandshake(const char* firstBytes = NULL, int len = 0); /** * @return the time when the socket was opened. diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp index 9769dd62262..77fc732e918 100644 --- a/src/mongo/util/net/ssl_manager.cpp +++ b/src/mongo/util/net/ssl_manager.cpp @@ -195,10 +195,10 @@ public: virtual SSLConnection* accept(Socket* socket, const char* initialBytes, int len); - virtual SSLPeerInfo parseAndValidatePeerCertificateDeprecated(const SSLConnection* conn, + virtual std::string parseAndValidatePeerCertificateDeprecated(const SSLConnection* conn, const std::string& remoteHost); - StatusWith<boost::optional<SSLPeerInfo>> parseAndValidatePeerCertificate( + StatusWith<boost::optional<std::string>> parseAndValidatePeerCertificate( SSL* conn, const std::string& remoteHost) final; virtual const SSLConfiguration& getSSLConfiguration() const { @@ -220,9 +220,6 @@ public: virtual void SSL_free(SSLConnection* conn); private: - const int _rolesNid = OBJ_create(mongodbRolesOID.identifier.c_str(), - mongodbRolesOID.shortDescription.c_str(), - mongodbRolesOID.longDescription.c_str()); UniqueSSLContext _serverContext; // SSL context for incoming connections UniqueSSLContext _clientContext; // SSL context for outgoing connections std::string _password; @@ -271,9 +268,6 @@ private: std::string* subjectName, Date_t* serverNotAfter); - - StatusWith<std::unordered_set<RoleName>> _parsePeerRoles(X509* peerCert) const; - /** @return true if was successful, otherwise false */ bool _setupPEM(SSL_CTX* context, const std::string& keyFile, const std::string& password); @@ -1095,7 +1089,7 @@ bool SSLManager::_hostNameMatch(const char* nameToMatch, const char* certHostNam } } -StatusWith<boost::optional<SSLPeerInfo>> SSLManager::parseAndValidatePeerCertificate( +StatusWith<boost::optional<std::string>> SSLManager::parseAndValidatePeerCertificate( SSL* conn, const std::string& remoteHost) { if (!_sslConfiguration.hasCA && isSSLServer) return {boost::none}; @@ -1133,16 +1127,10 @@ StatusWith<boost::optional<SSLPeerInfo>> SSLManager::parseAndValidatePeerCertifi std::string peerSubjectName = getCertificateSubjectName(peerCert); LOG(2) << "Accepted TLS connection from peer: " << peerSubjectName; - StatusWith<std::unordered_set<RoleName>> swPeerCertificateRoles = _parsePeerRoles(peerCert); - if (!swPeerCertificateRoles.isOK()) { - return swPeerCertificateRoles.getStatus(); - } - // If this is an SSL client context (on a MongoDB server or client) // perform hostname validation of the remote server if (remoteHost.empty()) { - return boost::make_optional( - SSLPeerInfo(peerSubjectName, std::move(swPeerCertificateRoles.getValue()))); + return boost::make_optional(peerSubjectName); } // Try to match using the Subject Alternate Name, if it exists. @@ -1200,11 +1188,10 @@ StatusWith<boost::optional<SSLPeerInfo>> SSLManager::parseAndValidatePeerCertifi } } - return boost::make_optional(SSLPeerInfo(peerSubjectName, std::unordered_set<RoleName>())); + return boost::make_optional(peerSubjectName); } - -SSLPeerInfo SSLManager::parseAndValidatePeerCertificateDeprecated(const SSLConnection* conn, +std::string SSLManager::parseAndValidatePeerCertificateDeprecated(const SSLConnection* conn, const std::string& remoteHost) { auto swPeerSubjectName = parseAndValidatePeerCertificate(conn->ssl, remoteHost); // We can't use uassertStatusOK here because we need to throw a SocketException. @@ -1212,128 +1199,7 @@ SSLPeerInfo SSLManager::parseAndValidatePeerCertificateDeprecated(const SSLConne throw SocketException(SocketException::CONNECT_ERROR, swPeerSubjectName.getStatus().reason()); } - return swPeerSubjectName.getValue().get_value_or(SSLPeerInfo()); -} - -StatusWith<std::unordered_set<RoleName>> SSLManager::_parsePeerRoles(X509* peerCert) const { - // exts is owned by the peerCert - STACK_OF(X509_EXTENSION)* exts = peerCert->cert_info->extensions; - - int extCount = 0; - if (exts) { - extCount = sk_X509_EXTENSION_num(exts); - } - - ASN1_OBJECT* rolesObj = OBJ_nid2obj(_rolesNid); - - // Search all certificate extensions for our own - std::unordered_set<RoleName> roles; - for (int i = 0; i < extCount; i++) { - X509_EXTENSION* ex = sk_X509_EXTENSION_value(exts, i); - ASN1_OBJECT* obj = X509_EXTENSION_get_object(ex); - - if (!OBJ_cmp(obj, rolesObj)) { - // We've found an extension which has our roles OID - ASN1_OCTET_STRING* data = X509_EXTENSION_get_data(ex); - - /* - * MongoDBAuthorizationGrant ::= CHOICE { - * MongoDBRole, - * ...!UTF8String:"Unrecognized entity in MongoDBAuthorizationGrant" - * } - * MongoDBAuthorizationGrants ::= SET OF MongoDBAuthorizationGrant - */ - // Extract the set of roles from our extension, and load them into an OpenSSL stack. - STACK_OF(ASN1_TYPE)* mongoDBAuthorizationGrants = nullptr; - - // OpenSSL's parsing function will try and manipulate the pointer it's passed. If we - // passed it 'data->data' directly, it would modify structures owned by peerCert. - const unsigned char* dataBytes = data->data; - mongoDBAuthorizationGrants = - d2i_ASN1_SET_ANY(&mongoDBAuthorizationGrants, &dataBytes, data->length); - if (!mongoDBAuthorizationGrants) { - return Status(ErrorCodes::FailedToParse, - "Failed to parse x509 authorization grants"); - } - const auto grantGuard = MakeGuard([&mongoDBAuthorizationGrants]() { - sk_ASN1_TYPE_pop_free(mongoDBAuthorizationGrants, ASN1_TYPE_free); - }); - - /* - * MongoDBRole ::= SEQUENCE { - * role UTF8String, - * database UTF8String - * } - */ - // Loop through every role in the stack. - ASN1_TYPE* MongoDBRoleWrapped = nullptr; - while ((MongoDBRoleWrapped = sk_ASN1_TYPE_pop(mongoDBAuthorizationGrants))) { - const auto roleWrappedGuard = - MakeGuard([MongoDBRoleWrapped]() { ASN1_TYPE_free(MongoDBRoleWrapped); }); - - if (MongoDBRoleWrapped->type == V_ASN1_SEQUENCE) { - // Unwrap the ASN1Type into a STACK_OF(ASN1_TYPE) - unsigned char* roleBytes = ASN1_STRING_data(MongoDBRoleWrapped->value.sequence); - int roleBytesLength = ASN1_STRING_length(MongoDBRoleWrapped->value.sequence); - ASN1_SEQUENCE_ANY* MongoDBRole = nullptr; - MongoDBRole = d2i_ASN1_SEQUENCE_ANY( - &MongoDBRole, (const unsigned char**)&roleBytes, roleBytesLength); - if (!MongoDBRole) { - return Status(ErrorCodes::FailedToParse, - "Failed to parse role in x509 authorization grant"); - } - const auto roleGuard = MakeGuard( - [&MongoDBRole]() { sk_ASN1_TYPE_pop_free(MongoDBRole, ASN1_TYPE_free); }); - - if (sk_ASN1_TYPE_num(MongoDBRole) != 2) { - return Status(ErrorCodes::FailedToParse, - "Role entity in MongoDBAuthorizationGrant must have exactly " - "2 sequence elements"); - } - // Extract the subcomponents of the sequence, which are popped off the stack in - // reverse order. Here, parse the role's database. - ASN1_TYPE* roleComponent = sk_ASN1_TYPE_pop(MongoDBRole); - const auto roleDBGuard = - MakeGuard([roleComponent]() { ASN1_TYPE_free(roleComponent); }); - if (roleComponent->type != V_ASN1_UTF8STRING) { - return Status(ErrorCodes::FailedToParse, - "database in MongoDBRole must be a UTF8 string"); - } - std::string roleDB( - reinterpret_cast<char*>(ASN1_STRING_data(roleComponent->value.utf8string))); - - // Parse the role's name. - roleComponent = sk_ASN1_TYPE_pop(MongoDBRole); - const auto roleNameGuard = - MakeGuard([roleComponent]() { ASN1_TYPE_free(roleComponent); }); - if (roleComponent->type != V_ASN1_UTF8STRING) { - return Status(ErrorCodes::FailedToParse, - "role in MongoDBRole must be a UTF8 string"); - } - std::string roleName( - reinterpret_cast<char*>(ASN1_STRING_data(roleComponent->value.utf8string))); - - // Construct a RoleName from the subcomponents - roles.emplace(RoleName(roleName, roleDB)); - - } else { - return Status(ErrorCodes::FailedToParse, - "Unrecognized entity in MongoDBAuthorizationGrant"); - } - } - } - } - - LOG(1) << "MONGODB-X509 authorization parsed the following roles from peer certificate: " - << [&roles]() { - StringBuilder sb; - std::for_each(roles.begin(), roles.end(), [&sb](const RoleName& role) { - sb << role.toString(); - }); - return sb.str(); - }(); - - return roles; + return swPeerSubjectName.getValue().get_value_or(""); } std::string SSLManagerInterface::getSSLErrorMessage(int code) { diff --git a/src/mongo/util/net/ssl_manager.h b/src/mongo/util/net/ssl_manager.h index ef7ad5c403b..ecc393070a8 100644 --- a/src/mongo/util/net/ssl_manager.h +++ b/src/mongo/util/net/ssl_manager.h @@ -40,7 +40,6 @@ #include "mongo/bson/bsonobj.h" #include "mongo/util/decorable.h" #include "mongo/util/net/sock.h" -#include "mongo/util/net/ssl_types.h" #include "mongo/util/time_support.h" #include <openssl/err.h> @@ -88,24 +87,6 @@ struct SSLConfiguration { bool hasCA = false; }; -/** - * Stores information about a globally unique OID. - */ -class ASN1OID { -public: - ASN1OID(std::string identifier, std::string shortDescription, std::string longDescription) - : identifier(std::move(identifier)), - shortDescription(std::move(shortDescription)), - longDescription(std::move(longDescription)) {} - - std::string identifier; // An OID - std::string shortDescription; // A brief description of the entity associated with the OID - std::string longDescription; // A long form description of the entity associated with the OID -}; -const ASN1OID mongodbRolesOID("1.3.6.1.4.1.34601.2.1.1", - "MongoRoles", - "Sequence of MongoDB Database Roles"); - class SSLManagerInterface : public Decorable<SSLManagerInterface> { public: static std::unique_ptr<SSLManagerInterface> create(const SSLParams& params, bool isServer); @@ -135,7 +116,7 @@ public: * SocketException upon failure. New code should prefer the version that returns * a StatusWith instead. */ - virtual SSLPeerInfo parseAndValidatePeerCertificateDeprecated( + virtual std::string parseAndValidatePeerCertificateDeprecated( const SSLConnection* conn, const std::string& remoteHost) = 0; /** @@ -181,10 +162,9 @@ public: * Fetches a peer certificate and validates it if it exists. If validation fails, but weak * validation is enabled, boost::none will be returned. If validation fails, and invalid * certificates are not allowed, a non-OK status will be returned. If validation is successful, - * an engaged optional containing the certificate's subject name, and any roles acquired by - * X509 authorization will be returned. + * an engaged optional containing the certificate's subject name will be returned. */ - virtual StatusWith<boost::optional<SSLPeerInfo>> parseAndValidatePeerCertificate( + virtual StatusWith<boost::optional<std::string>> parseAndValidatePeerCertificate( SSL* ssl, const std::string& remoteHost) = 0; }; diff --git a/src/mongo/util/net/ssl_types.h b/src/mongo/util/net/ssl_types.h deleted file mode 100644 index 87fe6eca59a..00000000000 --- a/src/mongo/util/net/ssl_types.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright 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> -#include <unordered_set> - -#include "mongo/db/auth/role_name.h" - -namespace mongo { - -/** - * Contains information extracted from the peer certificate which is consumed by subsystems - * outside of the networking stack. - */ -struct SSLPeerInfo { - SSLPeerInfo(std::string subjectName, std::unordered_set<RoleName> roles) - : subjectName(std::move(subjectName)), roles(std::move(roles)) {} - SSLPeerInfo() = default; - - std::string subjectName; - std::unordered_set<RoleName> roles; -}; - -} // namespace mongo |