aboutsummaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
-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
-rw-r--r--src/modules/m_gateway.cpp21
-rw-r--r--src/modules/m_sasl.cpp8
-rw-r--r--src/modules/m_spanningtree/compat.cpp29
-rw-r--r--src/modules/m_sslinfo.cpp69
-rw-r--r--src/modules/m_sslmodes.cpp11
8 files changed, 195 insertions, 84 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));
diff --git a/src/modules/m_gateway.cpp b/src/modules/m_gateway.cpp
index 33e2f10f7..78313dc68 100644
--- a/src/modules/m_gateway.cpp
+++ b/src/modules/m_gateway.cpp
@@ -107,9 +107,24 @@ public:
return false;
// Does the user have a valid fingerprint?
- const std::string fp = sslapi ? sslapi->GetFingerprint(user) : "";
- if (!fingerprint.empty() && !InspIRCd::TimingSafeCompare(fp, fingerprint))
- return false;
+ if (!fingerprint.empty())
+ {
+ if (!sslapi)
+ return false;
+
+ bool okay = false;
+ for (const auto& fp : sslapi->GetFingerprints(user))
+ {
+ if (InspIRCd::TimingSafeCompare(fp, fingerprint))
+ {
+ okay = true;
+ break;
+ }
+ }
+
+ if (!okay)
+ return false;
+ }
for (const auto& mask : hostmasks)
{
diff --git a/src/modules/m_sasl.cpp b/src/modules/m_sasl.cpp
index ac2b4dd4e..61f529643 100644
--- a/src/modules/m_sasl.cpp
+++ b/src/modules/m_sasl.cpp
@@ -200,9 +200,11 @@ public:
std::vector<std::string> params;
params.push_back(method);
- const std::string fp = sslapi ? sslapi->GetFingerprint(user) : "";
- if (!fp.empty())
- params.push_back(fp);
+ if (sslapi)
+ {
+ for (const auto& fingerprint : sslapi->GetFingerprints(user))
+ params.push_back(fingerprint);
+ }
SendSASL(user, "*", 'S', params);
}
diff --git a/src/modules/m_spanningtree/compat.cpp b/src/modules/m_spanningtree/compat.cpp
index 917b1e480..5d61272ea 100644
--- a/src/modules/m_spanningtree/compat.cpp
+++ b/src/modules/m_spanningtree/compat.cpp
@@ -71,6 +71,35 @@ void TreeSocket::WriteLine(const std::string& original_line)
// FRHOST was introduced in PROTO_INSPIRCD_4; drop it.
return;
}
+ else if (irc::equals(command, "METADATA"))
+ {
+ // :<sid> METADATA <uuid|chan|*|@> <name> :<value>
+ size_t targetend = NextToken(line, cmdend);
+ size_t nameend = NextToken(line, targetend);
+ size_t flagend = NextToken(line, nameend);
+ if (flagend != std::string::npos)
+ {
+ std::string extname(line, targetend + 1, nameend - targetend - 1);
+ if (irc::equals(extname, "ssl_cert"))
+ {
+ // Check we have the "e" flag (no error).
+ if (line.find('e', nameend + 1) < flagend)
+ {
+ size_t fpend = NextToken(line, flagend);
+ if (fpend != std::string::npos)
+ {
+ size_t commapos = line.find(',', flagend + 1);
+ if (commapos < fpend)
+ {
+ // Multiple fingerprints in ssl_cert was introduced in PROTO_INSPIRCD_4; drop it.
+ line.erase(commapos, fpend - commapos);
+ }
+
+ }
+ }
+ }
+ }
+ }
else if (irc::equals(command, "SQUERY"))
{
// SQUERY was introduced in PROTO_INSPIRCD_4; convert to PRIVMSG.
diff --git a/src/modules/m_sslinfo.cpp b/src/modules/m_sslinfo.cpp
index d6454e1c6..9c370c4ee 100644
--- a/src/modules/m_sslinfo.cpp
+++ b/src/modules/m_sslinfo.cpp
@@ -83,7 +83,7 @@ public:
<< " ";
if (cert->GetError().empty())
- value << cert->GetFingerprint() << " " << cert->GetDN() << " " << cert->GetIssuer();
+ value << insp::join(cert->GetFingerprints(), ',') << " " << cert->GetDN() << " " << cert->GetIssuer();
else
value << cert->GetError();
@@ -117,7 +117,12 @@ public:
}
else
{
- getline(s, cert->fingerprint, ' ');
+ std::string fingerprints;
+ getline(s, fingerprints, ' ');
+ irc::commasepstream fingerprintstream(fingerprints);
+ for (std::string fingerprint; fingerprintstream.GetToken(fingerprint); )
+ cert->fingerprints.push_back(fingerprint);
+
getline(s, cert->dn, ' ');
getline(s, cert->issuer, '\n');
}
@@ -211,7 +216,8 @@ private:
{
source->WriteNotice("*** Distinguished Name: " + cert->GetDN());
source->WriteNotice("*** Issuer: " + cert->GetIssuer());
- source->WriteNotice("*** Key Fingerprint: " + cert->GetFingerprint());
+ for (const auto& fingerprint : cert->GetFingerprints())
+ source->WriteNotice("*** Key Fingerprint: " + fingerprint);
}
}
@@ -306,14 +312,23 @@ class ModuleSSLInfo final
{
private:
CommandSSLInfo cmd;
- std::string hash;
- bool spkifp;
+ std::vector<std::string> hashes;
unsigned long warnexpiring;
bool welcomemsg;
- static bool MatchFP(ssl_cert* const cert, const std::string& fp)
+ static bool MatchFingerprint(const ssl_cert* cert, const std::string& fp)
{
- return irc::spacesepstream(fp).Contains(cert->GetFingerprint());
+ irc::spacesepstream configfpstream(fp);
+ for (std::string configfp; configfpstream.GetToken(configfp); )
+ {
+ for (const auto& certfp : cert->GetFingerprints())
+ {
+ if (InspIRCd::TimingSafeCompare(certfp, configfp))
+ return true;
+ }
+ }
+
+ return false;
}
public:
@@ -331,10 +346,19 @@ public:
const auto& tag = ServerInstance->Config->ConfValue("sslinfo");
cmd.operonlyfp = tag->getBool("operonly");
cmd.sslapi.localsecure = tag->getBool("localsecure", true);
- hash = tag->getString("hash");
- spkifp = tag->getBool("spkifp");
warnexpiring = tag->getDuration("warnexpiring", 0, 0, 60*60*24*365);
welcomemsg = tag->getBool("welcomemsg");
+
+ hashes.clear();
+ irc::spacesepstream hashstream(tag->getString("hash"));
+ for (std::string hash; hashstream.GetToken(hash); )
+ {
+ if (!hash.compare(0, 5, "spki-", 5))
+ hash.insert(4, "fp"); // spki-foo => spkifp-foo
+ else
+ hash.insert(0, "certfp-"); // foo => certfp-foo
+ hashes.push_back(hash);
+ }
}
void OnWhois(Whois::Context& whois) override
@@ -345,8 +369,11 @@ public:
ssl_cert* cert = cmd.sslapi.GetCertificate(whois.GetTarget());
if (cert)
{
- if ((!cmd.operonlyfp || whois.IsSelfWhois() || whois.GetSource()->IsOper()) && !cert->fingerprint.empty())
- whois.SendLine(RPL_WHOISCERTFP, INSP_FORMAT("has TLS client certificate fingerprint {}", cert->fingerprint));
+ if (!cmd.operonlyfp || whois.IsSelfWhois() || whois.GetSource()->IsOper())
+ {
+ for (const auto& fingerprint : cert->GetFingerprints())
+ whois.SendLine(RPL_WHOISCERTFP, INSP_FORMAT("has TLS client certificate fingerprint {}", fingerprint));
+ }
}
}
@@ -377,7 +404,7 @@ public:
}
const std::string fingerprint = oper->GetConfig()->getString("fingerprint");
- if (!fingerprint.empty() && (!cert || !MatchFP(cert, fingerprint)))
+ if (!fingerprint.empty() && (!cert || !MatchFingerprint(cert, fingerprint)))
{
if (!automatic)
{
@@ -479,24 +506,24 @@ public:
// Create a fake ssl_cert for the user.
auto* cert = new ssl_cert();
- if (!hash.empty())
+ cert->dn = "(unknown)";
+ cert->invalid = false;
+ cert->issuer = "(unknown)";
+ cert->trusted = true;
+ cert->unknownsigner = false;
+ for (const auto& hash : hashes)
{
- iter = flags->find(spkifp ? "spkifp-" : "certfp-" + hash);
+ iter = flags->find(hash);
if (iter != flags->end() && !iter->second.empty())
{
// If the gateway specifies this flag we put all trust onto them
// for having validated the client certificate. This is probably
// ill-advised but there's not much else we can do.
- cert->fingerprint = iter->second;
- cert->dn = "(unknown)";
- cert->invalid = false;
- cert->issuer = "(unknown)";
- cert->trusted = true;
- cert->unknownsigner = false;
+ cert->fingerprints.push_back(iter->second);
}
}
- if (cert->fingerprint.empty())
+ if (cert->GetFingerprints().empty())
{
cert->error = "WebIRC gateway did not send a client fingerprint";
cert->revoked = true;
diff --git a/src/modules/m_sslmodes.cpp b/src/modules/m_sslmodes.cpp
index d5037b195..ff84b5845 100644
--- a/src/modules/m_sslmodes.cpp
+++ b/src/modules/m_sslmodes.cpp
@@ -56,8 +56,15 @@ public:
bool IsMatch(User* user, Channel* channel, const std::string& text) override
{
- const std::string fp = sslapi ? sslapi->GetFingerprint(user) : "";
- return !fp.empty() && InspIRCd::Match(fp, text);
+ if (!sslapi)
+ return false;
+
+ for (const auto& fingerprint : sslapi->GetFingerprints(user))
+ {
+ if (InspIRCd::Match(fingerprint, text))
+ return true;
+ }
+ return false;
}
};