aboutsummaryrefslogtreecommitdiff
path: root/src/modules/extra
diff options
context:
space:
mode:
authorGravatar Sadie Powell2024-02-19 18:46:05 +0000
committerGravatar Sadie Powell2024-02-19 18:46:05 +0000
commitebd03383f856bfaa72b1700a96561396e6f8f6cc (patch)
tree47651f7a9c8567ae020c6b8750db58a7d3c2da2b /src/modules/extra
parentFix 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.cpp74
-rw-r--r--src/modules/extra/m_ssl_mbedtls.cpp35
-rw-r--r--src/modules/extra/m_ssl_openssl.cpp32
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));