diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2018-05-07 18:51:11 -0400 |
---|---|---|
committer | Sara Golemon <sara.golemon@mongodb.com> | 2018-05-15 22:11:45 -0400 |
commit | fa3fae04c144048bebf3f4c58e08b7b0c32743e1 (patch) | |
tree | 32cc44010126bb4e5d94e0c181aeb210f34edab8 /src/mongo/util/net/ssl_manager.cpp | |
parent | cf339b8a8d8708e8b28747fe0cafee7cc79fe9a6 (diff) | |
download | mongo-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.cpp | 123 |
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(); |