diff options
| author | 2024-02-19 18:46:05 +0000 | |
|---|---|---|
| committer | 2024-02-19 18:46:05 +0000 | |
| commit | ebd03383f856bfaa72b1700a96561396e6f8f6cc (patch) | |
| tree | 47651f7a9c8567ae020c6b8750db58a7d3c2da2b /src/modules/extra | |
| parent | Fix more issues with the v3 compat layer. (diff) | |
Allow using multiple SSL fingerprint algorithms.
Closes #1804.
Diffstat (limited to 'src/modules/extra')
| -rw-r--r-- | src/modules/extra/m_ssl_gnutls.cpp | 74 | ||||
| -rw-r--r-- | src/modules/extra/m_ssl_mbedtls.cpp | 35 | ||||
| -rw-r--r-- | src/modules/extra/m_ssl_openssl.cpp | 32 |
3 files changed, 86 insertions, 55 deletions
diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp index 2cd3badaf..3078ae824 100644 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@ -117,24 +117,37 @@ namespace GnuTLS class Hash final { - gnutls_digest_algorithm_t hash; + std::vector<std::pair<gnutls_digest_algorithm_t, bool>> hashes; public: // Nothing to deallocate, constructor may throw freely - Hash(const std::string& hashname) + Hash(const std::string& hashlist) { - hash = gnutls_digest_get_id(hashname.c_str()); - if (hash == GNUTLS_DIG_UNKNOWN) - throw Exception("Unknown hash type " + hashname); + irc::spacesepstream hashstream(hashlist); + for (std::string hashname; hashstream.GetToken(hashname); ) + { + bool spki = false; + if (!hashname.compare(0, 5, "spki-", 5) && hashname.length() > 5) + { + spki = true; + hashname.erase(0, 5); + } + + auto hash = gnutls_digest_get_id(hashname.c_str()); + if (hash == GNUTLS_DIG_UNKNOWN) + throw Exception("Unknown hash type " + hashname); + + // Check if the user is giving us something that is a valid MAC but not digest + gnutls_hash_hd_t is_digest; + if (gnutls_hash_init(&is_digest, hash) < 0) + throw Exception("Unknown hash type " + hashname); + gnutls_hash_deinit(is_digest, nullptr); - // Check if the user is giving us something that is a valid MAC but not digest - gnutls_hash_hd_t is_digest; - if (gnutls_hash_init(&is_digest, hash) < 0) - throw Exception("Unknown hash type " + hashname); - gnutls_hash_deinit(is_digest, nullptr); + hashes.emplace_back(hash, spki); + } } - gnutls_digest_algorithm_t get() const { return hash; } + std::vector<std::pair<gnutls_digest_algorithm_t, bool>> get() const { return hashes; } }; #ifndef GNUTLS_AUTO_DH @@ -482,9 +495,6 @@ namespace GnuTLS */ const bool requestclientcert; - /** Whether the fingerprint is a SPKI fingerprint or not. */ - bool spkifp; - static std::string ReadFile(const std::string& filename) { auto file = ServerInstance->Config->ReadFile(filename, ServerInstance->Time()); @@ -538,7 +548,6 @@ namespace GnuTLS unsigned int outrecsize; bool requestclientcert; - bool spkifp; Config(const std::string& profilename, const std::shared_ptr<ConfigTag>& tag) : name(profilename) @@ -551,7 +560,6 @@ namespace GnuTLS , mindh(tag->getNum<unsigned int>("mindhbits", 1024)) , hashstr(tag->getString("hash", "sha256", 1)) , requestclientcert(tag->getBool("requestclientcert", true)) - , spkifp(tag->getBool("spkifp")) { // Load trusted CA and revocation list, if set std::string filename = tag->getString("cafile"); @@ -576,7 +584,6 @@ namespace GnuTLS , priority(config.priostr) , outrecsize(config.outrecsize) , requestclientcert(config.requestclientcert) - , spkifp(config.spkifp) { #ifndef GNUTLS_AUTO_DH x509cred.SetDH(config.dh); @@ -598,9 +605,8 @@ namespace GnuTLS const std::string& GetName() const { return name; } X509Credentials& GetX509Credentials() { return x509cred; } - gnutls_digest_algorithm_t GetHash() const { return hash.get(); } + std::vector<std::pair<gnutls_digest_algorithm_t, bool>> GetHash() const { return hash.get(); } unsigned int GetOutgoingRecordSize() const { return outrecsize; } - bool UseSPKI() const { return spkifp; } }; } @@ -669,12 +675,12 @@ private: } } - int GetCertFP(gnutls_x509_crt_t& cert, char* buffer, size_t& buffer_size) + int GetCertFP(gnutls_x509_crt_t& cert, gnutls_digest_algorithm_t algo, char* buffer, size_t& buffer_size) { - return gnutls_x509_crt_get_fingerprint(cert, GetProfile().GetHash(), buffer, &buffer_size); + return gnutls_x509_crt_get_fingerprint(cert, algo, buffer, &buffer_size); } - int GetSPKIFP(gnutls_x509_crt_t& cert, char* buffer, size_t& buffer_size) + int GetSPKIFP(gnutls_x509_crt_t& cert, gnutls_digest_algorithm_t algo, char* buffer, size_t& buffer_size) { int ret; gnutls_pubkey_t pubkey; @@ -698,10 +704,10 @@ private: return ret; gnutls_pubkey_deinit(pubkey); - if (gnutls_hash_fast(GetProfile().GetHash(), &derkey[0], derkey_size, buffer) != GNUTLS_E_SUCCESS) + if (gnutls_hash_fast(algo, &derkey[0], derkey_size, buffer) != GNUTLS_E_SUCCESS) return ret; - buffer_size = gnutls_hash_get_len(GetProfile().GetHash()); + buffer_size = gnutls_hash_get_len(algo); return GNUTLS_E_SUCCESS; } @@ -762,15 +768,19 @@ private: if (gnutls_x509_crt_get_issuer_dn(cert, buffer, &buffer_size) == 0) ProcessDNString(buffer, buffer_size, certinfo->issuer); - buffer_size = sizeof(buffer); - ret = GetProfile().UseSPKI() ? GetSPKIFP(cert, buffer, buffer_size) : GetCertFP(cert, buffer, buffer_size); - if (ret != GNUTLS_E_SUCCESS) - { - certinfo->error = gnutls_strerror(ret); - } - else + + for (const auto& [hash, spki] : GetProfile().GetHash()) { - certinfo->fingerprint = Hex::Encode(buffer, buffer_size); + buffer_size = sizeof(buffer); + ret = spki ? GetSPKIFP(cert, hash, buffer, buffer_size) : GetCertFP(cert, hash, buffer, buffer_size); + if (ret != GNUTLS_E_SUCCESS) + { + certinfo->error = gnutls_strerror(ret); + } + else + { + certinfo->fingerprints.push_back(Hex::Encode(buffer, buffer_size)); + } } certinfo->activation = gnutls_x509_crt_get_activation_time(cert); diff --git a/src/modules/extra/m_ssl_mbedtls.cpp b/src/modules/extra/m_ssl_mbedtls.cpp index 9e72cb49c..5890db067 100644 --- a/src/modules/extra/m_ssl_mbedtls.cpp +++ b/src/modules/extra/m_ssl_mbedtls.cpp @@ -368,27 +368,40 @@ namespace mbedTLS class Hash final { - const mbedtls_md_info_t* md; + private: + std::vector<std::pair<const mbedtls_md_info_t*, unsigned char>> mds; /** Buffer where cert hashes are written temporarily */ mutable std::vector<unsigned char> buf; public: - Hash(std::string hashstr) + Hash(const std::string& hashstr) { - std::transform(hashstr.begin(), hashstr.end(), hashstr.begin(), ::toupper); - md = mbedtls_md_info_from_string(hashstr.c_str()); - if (!md) - throw Exception("Unknown hash: " + hashstr); + irc::spacesepstream hashstream(hashstr); + unsigned char mdsize = 0; + for (std::string hash; hashstream.GetToken(hash); ) + { + std::transform(hash.begin(), hash.end(), hash.begin(), ::toupper); + const auto* md = mbedtls_md_info_from_string(hash.c_str()); + if (!md) + throw Exception("Unknown hash: " + hash); + + mds.push_back(std::make_pair(md, mbedtls_md_get_size(md))); + mdsize = std::max(mdsize, mds.back().second); + } - buf.resize(mbedtls_md_get_size(md)); + buf.resize(mdsize); + buf.shrink_to_fit(); } - std::string hash(const unsigned char* input, size_t length) const + void hash(const unsigned char* input, size_t length, std::vector<std::string>& fingerprints) const { - mbedtls_md(md, input, length, &buf.front()); - return Hex::Encode(&buf.front(), buf.size()); + for (const auto& [md, size] : mds) + { + mbedtls_md(md, input, length, &buf.front()); + fingerprints.push_back(Hex::Encode(&buf.front(), size)); + } } }; @@ -627,7 +640,7 @@ private: } // If there is a certificate we can always generate a fingerprint - certificate->fingerprint = GetProfile().GetHash().hash(cert->raw.p, cert->raw.len); + GetProfile().GetHash().hash(cert->raw.p, cert->raw.len, certificate->fingerprints); // At this point mbedTLS verified the cert already, we just need to check the results const uint32_t flags = mbedtls_ssl_get_verify_result(&sess); diff --git a/src/modules/extra/m_ssl_openssl.cpp b/src/modules/extra/m_ssl_openssl.cpp index 9f7240583..857755c0f 100644 --- a/src/modules/extra/m_ssl_openssl.cpp +++ b/src/modules/extra/m_ssl_openssl.cpp @@ -303,7 +303,7 @@ namespace OpenSSL /** Digest to use when generating fingerprints */ - const EVP_MD* digest; + std::vector<const EVP_MD*> digests; /** Last error, set by error_callback() */ @@ -382,10 +382,15 @@ namespace OpenSSL throw Exception("Couldn't set DH parameters"); #endif - const std::string hash = tag->getString("hash", "sha256", 1); - digest = EVP_get_digestbyname(hash.c_str()); - if (!digest) - throw Exception("Unknown hash type " + hash); + irc::spacesepstream hashstream(tag->getString("hash", "sha256", 1)); + for (std::string hash; hashstream.GetToken(hash); ) + { + const auto* digest = EVP_get_digestbyname(hash.c_str()); + if (!digest) + throw Exception("Unknown hash type " + hash); + + digests.push_back(digest); + } const std::string ciphers = tag->getString("ciphers"); if (!ciphers.empty()) @@ -455,7 +460,7 @@ namespace OpenSSL const std::string& GetName() const { return name; } SSL* CreateServerSession() { return ctx.CreateServerSession(); } SSL* CreateClientSession() { return clientctx.CreateClientSession(); } - const EVP_MD* GetDigest() { return digest; } + const std::vector<const EVP_MD*> GetDigests() { return digests; } bool AllowRenegotiation() const { return allowrenego; } unsigned int GetOutgoingRecordSize() const { return outrecsize; } }; @@ -613,13 +618,16 @@ private: GetDNString(X509_get_subject_name(cert), certinfo->dn); GetDNString(X509_get_issuer_name(cert), certinfo->issuer); - if (!X509_digest(cert, GetProfile().GetDigest(), md, &n)) - { - certinfo->error = "Out of memory generating fingerprint"; - } - else + for (const auto* digest : GetProfile().GetDigests()) { - certinfo->fingerprint = Hex::Encode(md, n); + if (!X509_digest(cert, digest, md, &n)) + { + certinfo->error = "Out of memory generating fingerprint"; + } + else + { + certinfo->fingerprints.push_back(Hex::Encode(md, n)); + } } certinfo->activation = GetTime(X509_getm_notBefore(cert)); |
