summaryrefslogtreecommitdiff
path: root/src/mongo/util/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/util/net')
-rw-r--r--src/mongo/util/net/SConscript5
-rw-r--r--src/mongo/util/net/ssl_manager.cpp71
-rw-r--r--src/mongo/util/net/ssl_options.h20
-rw-r--r--src/mongo/util/net/ssl_parameters.cpp151
-rw-r--r--src/mongo/util/net/ssl_parameters.idl44
5 files changed, 286 insertions, 5 deletions
diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript
index c120b65990f..b80beed6dc6 100644
--- a/src/mongo/util/net/SConscript
+++ b/src/mongo/util/net/SConscript
@@ -22,7 +22,9 @@ env.Library(
"socket_exception.cpp",
"ssl_manager.cpp",
"ssl_options.cpp",
+ "ssl_parameters.cpp",
"thread_idle_callback.cpp",
+ env.Idlc('ssl_parameters.idl')[0],
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
@@ -33,6 +35,9 @@ env.Library(
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/bson/dotted_path_support',
'$BUILD_DIR/mongo/db/server_options_core',
+ '$BUILD_DIR/mongo/db/server_parameters',
+ '$BUILD_DIR/mongo/crypto/sha256_block',
+ '$BUILD_DIR/mongo/idl/idl_parser',
'$BUILD_DIR/mongo/util/background_job',
'$BUILD_DIR/mongo/util/fail_point',
'$BUILD_DIR/mongo/util/options_parser/options_parser',
diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp
index 70854fd0a50..3ad394a05f1 100644
--- a/src/mongo/util/net/ssl_manager.cpp
+++ b/src/mongo/util/net/ssl_manager.cpp
@@ -88,6 +88,7 @@ std::string removeFQDNRoot(std::string name) {
return name;
};
+#ifdef MONGO_CONFIG_SSL
struct UniqueX509StoreCtxDeleter {
void operator()(X509_STORE_CTX* ctx) {
if (ctx) {
@@ -96,6 +97,7 @@ struct UniqueX509StoreCtxDeleter {
}
};
using UniqueX509StoreCtx = std::unique_ptr<X509_STORE_CTX, UniqueX509StoreCtxDeleter>;
+#endif
// Because the hostname having a slash is used by `mongo::SockAddr` to determine if a hostname is a
// Unix Domain Socket endpoint, this function uses the same logic. (See
@@ -1582,6 +1584,68 @@ StatusWith<TLSVersion> mapTLSVersion(const SSL* conn) {
}
}
+namespace {
+Status _validatePeerRoles(const stdx::unordered_set<RoleName>& embeddedRoles, SSL* conn) {
+ if (embeddedRoles.empty()) {
+ // Nothing offered, nothing to restrict.
+ return Status::OK();
+ }
+
+ if (!sslGlobalParams.tlsCATrusts) {
+ // Nothing restricted.
+ return Status::OK();
+ }
+
+ const auto& tlsCATrusts = sslGlobalParams.tlsCATrusts.get();
+ if (tlsCATrusts.empty()) {
+ // Nothing permitted.
+ return {ErrorCodes::BadValue,
+ "tlsCATrusts parameter prohibits role based authorization via X509 certificates"};
+ }
+
+ auto stack = SSLgetVerifiedChain(conn);
+ if (!stack || !sk_X509_num(stack.get())) {
+ return {ErrorCodes::BadValue, "Unable to obtain certificate chain"};
+ }
+
+ auto root = sk_X509_value(stack.get(), sk_X509_num(stack.get()) - 1);
+ SHA256Block::HashType digest;
+ if (!X509_digest(root, EVP_sha256(), digest.data(), nullptr)) {
+ return {ErrorCodes::BadValue, "Unable to digest root certificate"};
+ }
+
+ SHA256Block sha256(digest);
+ auto it = tlsCATrusts.find(sha256);
+ if (it == tlsCATrusts.end()) {
+ return {
+ ErrorCodes::BadValue,
+ str::stream() << "CA: " << sha256.toHexString()
+ << " is not authorized to grant any roles due to tlsCATrusts parameter"};
+ }
+
+ auto allowedRoles = it->second;
+ // See TLSCATrustsSetParameter::set() for a description of tlsCATrusts format.
+ if (allowedRoles.count(RoleName("", ""))) {
+ // CA is authorized for all role assignments.
+ return Status::OK();
+ }
+
+ for (const auto& role : embeddedRoles) {
+ // Check for exact match or wildcard matches.
+ if (!allowedRoles.count(role) && !allowedRoles.count(RoleName(role.getRole(), "")) &&
+ !allowedRoles.count(RoleName("", role.getDB()))) {
+ return {ErrorCodes::BadValue,
+ str::stream() << "CA: " << sha256.toHexString()
+ << " is not authorized to grant role "
+ << role.toString()
+ << " due to tlsCATrusts parameter"};
+ }
+ }
+
+ return Status::OK();
+}
+} // namespace
+
StatusWith<SSLPeerInfo> SSLManager::parseAndValidatePeerCertificate(
SSL* conn, const std::string& remoteHost, const HostAndPort& hostForLogging) {
auto sniName = getRawSNIServerName(conn);
@@ -1639,6 +1703,13 @@ StatusWith<SSLPeerInfo> SSLManager::parseAndValidatePeerCertificate(
return swPeerCertificateRoles.getStatus();
}
+ {
+ auto status = _validatePeerRoles(swPeerCertificateRoles.getValue(), conn);
+ if (!status.isOK()) {
+ return status;
+ }
+ }
+
// If this is an SSL client context (on a MongoDB server or client)
// perform hostname validation of the remote server
if (remoteHost.empty()) {
diff --git a/src/mongo/util/net/ssl_options.h b/src/mongo/util/net/ssl_options.h
index 5684e37636e..c95a3e728ad 100644
--- a/src/mongo/util/net/ssl_options.h
+++ b/src/mongo/util/net/ssl_options.h
@@ -32,9 +32,14 @@
#include "mongo/util/net/ssl_manager.h"
+#include <boost/optional.hpp>
+#include <map>
+#include <set>
#include <vector>
#include "mongo/base/status.h"
+#include "mongo/crypto/sha256_block.h"
+#include "mongo/db/auth/role_name.h"
namespace mongo {
@@ -44,6 +49,8 @@ class Environment;
} // namespace optionenvironment
struct SSLParams {
+ using TLSCATrusts = std::map<SHA256Block, std::set<RoleName>>;
+
enum class Protocols { TLS1_0, TLS1_1, TLS1_2, TLS1_3 };
AtomicInt32 sslMode; // --sslMode - the TLS operation mode, see enum SSLModes
std::string sslPEMTempDHParam; // --setParameter OpenSSLDiffieHellmanParameters=file : PEM file
@@ -51,11 +58,14 @@ struct SSLParams {
std::string sslPEMKeyFile; // --sslPEMKeyFile
std::string sslPEMKeyPassword; // --sslPEMKeyPassword
std::string sslClusterFile; // --sslInternalKeyFile
- std::string sslClusterPassword; // --sslInternalKeyPassword
- std::string sslCAFile; // --sslCAFile
- std::string sslClusterCAFile; // --sslClusterCAFile
- std::string sslCRLFile; // --sslCRLFile
- std::string sslCipherConfig; // --sslCipherConfig
+ std::string sslClusterPassword; // --sslInternalKeyPassword
+ std::string sslCAFile; // --sslCAFile
+ std::string sslClusterCAFile; // --sslClusterCAFile
+ std::string sslCRLFile; // --sslCRLFile
+ std::string sslCipherConfig; // --sslCipherConfig
+
+ boost::optional<TLSCATrusts> tlsCATrusts; // --setParameter tlsCATrusts
+
std::vector<Protocols> sslDisabledProtocols; // --sslDisabledProtocols
std::vector<Protocols> tlsLogVersions; // --tlsLogVersion
bool sslWeakCertificateValidation = false; // --sslWeakCertificateValidation
diff --git a/src/mongo/util/net/ssl_parameters.cpp b/src/mongo/util/net/ssl_parameters.cpp
new file mode 100644
index 00000000000..ed1d4c64fe8
--- /dev/null
+++ b/src/mongo/util/net/ssl_parameters.cpp
@@ -0,0 +1,151 @@
+
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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 Server Side 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/platform/basic.h"
+
+#include "mongo/bson/json.h"
+#include "mongo/config.h"
+#include "mongo/db/server_options.h"
+#include "mongo/db/server_parameters.h"
+#include "mongo/util/net/ssl_manager.h"
+#include "mongo/util/net/ssl_options.h"
+#include "mongo/util/net/ssl_parameters_gen.h"
+
+namespace mongo {
+
+namespace {
+
+class TLSCATrustsSetParameter : public ServerParameter {
+public:
+ TLSCATrustsSetParameter()
+ : ServerParameter(ServerParameterSet::getGlobal(),
+ "tlsCATrusts",
+ true, // allowedToChangeAtStartup
+ false // allowedToChangeAtRuntime
+ ) {}
+
+ void append(OperationContext*, BSONObjBuilder&, const std::string&) final;
+ Status set(const BSONElement&) final;
+ Status setFromString(const std::string&) final;
+} tlsCATrustsSetParameter;
+
+void TLSCATrustsSetParameter::append(OperationContext*,
+ BSONObjBuilder& b,
+ const std::string& name) {
+ if (!sslGlobalParams.tlsCATrusts) {
+ b.appendNull(name);
+ return;
+ }
+
+ BSONArrayBuilder trusts;
+
+ for (const auto& cait : sslGlobalParams.tlsCATrusts.get()) {
+ BSONArrayBuilder roles;
+
+ for (const auto& rolename : cait.second) {
+ BSONObjBuilder role;
+ role.append("role", rolename.getRole());
+ role.append("db", rolename.getDB());
+ roles.append(role.obj());
+ }
+
+ BSONObjBuilder ca;
+ ca.append("sha256", cait.first.toHexString());
+ ca.append("roles", roles.arr());
+
+ trusts.append(ca.obj());
+ }
+
+ b.append(name, trusts.arr());
+}
+
+/**
+ * tlsCATrusts takes the form of an array of documents describing
+ * a set of roles which a given certificate authority may grant.
+ *
+ * [
+ * {
+ * "sha256": "0123456789abcdef...", // SHA256 digest of a CA, as hex.
+ * "roles": [ // Array of grantable RoleNames
+ * { role: "read", db: "foo" },
+ * { role: "readWrite", "db: "bar" },
+ * // etc...
+ * ],
+ * },
+ * // { "sha256": "...", roles: [...]}, // Additional documents...
+ * ]
+ *
+ * If this list has been set, and a client connects with a certificate
+ * containing roles which it has not been authorized to grant,
+ * then the connection will be refused.
+ *
+ * Wilcard roles may be defined by omitting the role and/or db portions:
+ *
+ * { role: "", db: "foo" } // May grant any role on the 'foo' DB.
+ * { role: "read", db: "" } // May grant 'read' role on any DB.
+ * { role: "", db: "" } // May grant any role on any DB.
+ */
+Status TLSCATrustsSetParameter::set(const BSONElement& element) try {
+ if ((element.type() != Object) || !element.Obj().couldBeArray()) {
+ return {ErrorCodes::BadValue, "Value must be an array"};
+ }
+
+ SSLParams::TLSCATrusts trusts;
+ for (const auto& trustElement : BSONArray(element.Obj())) {
+ if (trustElement.type() != Object) {
+ return {ErrorCodes::BadValue, "Value must be an array of trust definitions"};
+ }
+
+ IDLParserErrorContext ctx("tlsCATrusts");
+ auto trust = TLSCATrust::parse(ctx, trustElement.Obj());
+
+ if (trusts.find(trust.getSha256()) != trusts.end()) {
+ return {ErrorCodes::BadValue,
+ str::stream() << "Duplicate thumbprint: " << trust.getSha256().toString()};
+ }
+
+ const auto& roles = trust.getRoles();
+ trusts[std::move(trust.getSha256())] = std::set<RoleName>(roles.begin(), roles.end());
+ }
+
+ sslGlobalParams.tlsCATrusts = std::move(trusts);
+ return Status::OK();
+} catch (...) {
+ return exceptionToStatus();
+}
+
+Status TLSCATrustsSetParameter::setFromString(const std::string& json) try {
+ return set(BSON("" << fromjson(json)).firstElement());
+} catch (...) {
+ return exceptionToStatus();
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/util/net/ssl_parameters.idl b/src/mongo/util/net/ssl_parameters.idl
new file mode 100644
index 00000000000..21d376a7bc2
--- /dev/null
+++ b/src/mongo/util/net/ssl_parameters.idl
@@ -0,0 +1,44 @@
+# Copyright (C) 2020-present MongoDB, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the Server Side Public License, version 1,
+# as published by MongoDB, Inc.
+#
+# 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
+# Server Side Public License for more details.
+#
+# You should have received a copy of the Server Side Public License
+# along with this program. If not, see
+# <http://www.mongodb.com/licensing/server-side-public-license>.
+#
+# 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 Server Side 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.
+#
+
+global:
+ cpp_namespace: "mongo"
+ cpp_includes:
+ - "mongo/util/net/ssl_options.h"
+
+imports:
+ - "mongo/crypto/sha256_block.idl"
+ - "mongo/db/auth/auth_types.idl"
+
+structs:
+ TLSCATrust:
+ description:
+ strict: true
+ fields:
+ sha256: sha256BlockHex
+ roles: array<RoleName>