summaryrefslogtreecommitdiff
path: root/src/mongo/util/net/ssl_manager.cpp
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2018-05-07 18:51:11 -0400
committerSara Golemon <sara.golemon@mongodb.com>2018-05-15 22:11:45 -0400
commitfa3fae04c144048bebf3f4c58e08b7b0c32743e1 (patch)
tree32cc44010126bb4e5d94e0c181aeb210f34edab8 /src/mongo/util/net/ssl_manager.cpp
parentcf339b8a8d8708e8b28747fe0cafee7cc79fe9a6 (diff)
downloadmongo-fa3fae04c144048bebf3f4c58e08b7b0c32743e1.tar.gz
SERVER-34735 Extract structured data from X509 subject names
Diffstat (limited to 'src/mongo/util/net/ssl_manager.cpp')
-rw-r--r--src/mongo/util/net/ssl_manager.cpp123
1 files changed, 110 insertions, 13 deletions
diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp
index 79a99543475..ae3aa75d222 100644
--- a/src/mongo/util/net/ssl_manager.cpp
+++ b/src/mongo/util/net/ssl_manager.cpp
@@ -44,7 +44,6 @@
#include "mongo/util/net/ssl_types.h"
#include "mongo/util/text.h"
-
namespace mongo {
namespace {
@@ -107,6 +106,72 @@ public:
#ifdef MONGO_CONFIG_SSL
namespace {
+#if MONGO_CONFIG_SSL_PROVIDER == MONGO_CONFIG_SSL_PROVIDER_OPENSSL
+// OpenSSL has a more complete library of OID to SN mappings.
+std::string x509OidToShortName(const std::string& name) {
+ const auto* sn = OBJ_nid2sn(OBJ_txt2nid(entry.oid.c_str()));
+ if (!sn) {
+ return name;
+ }
+ return sn;
+}
+#else
+// On Apple/Windows we have to provide our own mapping.
+std::string x509OidToShortName(const std::string& name) {
+ static const std::map<std::string, std::string> kX509OidToShortNameMappings = {
+ {"0.9.2342.19200300.100.1.1", "UID"},
+ {"0.9.2342.19200300.100.1.25", "DC"},
+ {"1.2.840.113549.1.9.1", "emailAddress"},
+ {"2.5.4.3", "CN"},
+ {"2.5.4.6", "C"},
+ {"2.5.4.7", "L"},
+ {"2.5.4.8", "ST"},
+ {"2.5.4.9", "STREET"},
+ {"2.5.4.10", "O"},
+ {"2.5.4.11", "OU"},
+ };
+
+ auto it = kX509OidToShortNameMappings.find(name);
+ if (it == kX509OidToShortNameMappings.end()) {
+ return name;
+ }
+ return it->second;
+}
+#endif
+} // namespace
+
+StatusWith<std::string> SSLX509Name::getOID(StringData oid) const {
+ for (const auto& rdn : _entries) {
+ for (const auto& entry : rdn) {
+ if (entry.oid == oid) {
+ return entry.value;
+ }
+ }
+ }
+ return {ErrorCodes::KeyNotFound, "OID does not exist"};
+}
+
+StringBuilder& operator<<(StringBuilder& os, const SSLX509Name& name) {
+ std::string comma;
+ for (const auto& rdn : name._entries) {
+ std::string plus;
+ os << comma;
+ for (const auto& entry : rdn) {
+ os << plus << x509OidToShortName(entry.oid) << "=" << escapeRfc2253(entry.value);
+ plus = "+";
+ }
+ comma = ",";
+ }
+ return os;
+}
+
+std::string SSLX509Name::toString() const {
+ StringBuilder os;
+ os << *this;
+ return os.str();
+}
+
+namespace {
void canonicalizeClusterDN(std::vector<std::string>* dn) {
// remove all RDNs we don't care about
for (size_t i = 0; i < dn->size(); i++) {
@@ -121,30 +186,62 @@ void canonicalizeClusterDN(std::vector<std::string>* dn) {
}
std::stable_sort(dn->begin(), dn->end());
}
+
+constexpr StringData kOID_DC = "0.9.2342.19200300.100.1.25"_sd;
+constexpr StringData kOID_O = "2.5.4.10"_sd;
+constexpr StringData kOID_OU = "2.5.4.11"_sd;
+
+std::vector<SSLX509Name::Entry> canonicalizeClusterDN(
+ const std::vector<std::vector<SSLX509Name::Entry>>& entries) {
+ std::vector<SSLX509Name::Entry> ret;
+
+ for (const auto& rdn : entries) {
+ for (const auto& entry : rdn) {
+ if ((entry.oid != kOID_DC) && (entry.oid != kOID_O) && (entry.oid != kOID_OU)) {
+ continue;
+ }
+ ret.push_back(entry);
+ }
+ }
+ std::stable_sort(ret.begin(), ret.end());
+ return ret;
+}
} // namespace
+/**
+ * The behavior of isClusterMember() is subtly different when passed
+ * an SSLX509Name versus a StringData.
+ *
+ * The SSLX509Name version (immediately below) compares distinguished
+ * names in their raw, unescaped forms and provides a more reliable match.
+ *
+ * The StringData version attempts to do a simplified string compare
+ * with the serialized version of the server subject name.
+ *
+ * Because escaping is not checked in the StringData version,
+ * some not-strictly matching RDNs will appear to share O/OU/DC with the
+ * server subject name. Therefore, that variant should be called with care.
+ */
+bool SSLConfiguration::isClusterMember(const SSLX509Name& subject) const {
+ auto client = canonicalizeClusterDN(subject._entries);
+ auto server = canonicalizeClusterDN(serverSubjectName._entries);
+
+ return !client.empty() && (client == server);
+}
+
bool SSLConfiguration::isClusterMember(StringData subjectName) const {
std::vector<std::string> clientRDN = StringSplitter::split(subjectName.toString(), ",");
- std::vector<std::string> serverRDN = StringSplitter::split(serverSubjectName, ",");
+ std::vector<std::string> serverRDN = StringSplitter::split(serverSubjectName.toString(), ",");
canonicalizeClusterDN(&clientRDN);
canonicalizeClusterDN(&serverRDN);
- if (clientRDN.size() == 0 || clientRDN.size() != serverRDN.size()) {
- return false;
- }
-
- for (size_t i = 0; i < serverRDN.size(); i++) {
- if (clientRDN[i] != serverRDN[i]) {
- return false;
- }
- }
- return true;
+ return !clientRDN.empty() && (clientRDN == serverRDN);
}
BSONObj SSLConfiguration::getServerStatusBSON() const {
BSONObjBuilder security;
- security.append("SSLServerSubjectName", serverSubjectName);
+ security.append("SSLServerSubjectName", serverSubjectName.toString());
security.appendBool("SSLServerHasCertificateAuthority", hasCA);
security.appendDate("SSLServerCertificateExpirationDate", serverCertificateExpirationDate);
return security.obj();