/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2014 Matthew Martin * Copyright (C) 2013-2014 Attila Molnar * Copyright (C) 2013, 2019, 2021-2023, 2025 Sadie Powell * Copyright (C) 2012 Robby * Copyright (C) 2009-2010 Daniel De Graaf * Copyright (C) 2008 Robin Burchell * * This file is part of InspIRCd. InspIRCd is free software: you can * redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation, version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "inspircd.h" #include "modules/hash.h" #include "modules/ssl.h" #include "main.h" #include "link.h" #include "treesocket.h" const std::string& TreeSocket::GetOurChallenge() { return capab->ourchallenge; } void TreeSocket::SetOurChallenge(const std::string& c) { capab->ourchallenge = c; } const std::string& TreeSocket::GetTheirChallenge() { return capab->theirchallenge; } void TreeSocket::SetTheirChallenge(const std::string& c) { capab->theirchallenge = c; } std::string TreeSocket::MakePass(const std::string& password, const std::string& challenge) { /* This is a simple (maybe a bit hacky?) HMAC algorithm, thanks to jilles for * suggesting the use of HMAC to secure the password against various attacks. * * Note: If an sha256 provider is not available, we MUST fall back to plaintext with no * HMAC challenge/response. */ HashProvider* sha256 = ServerInstance->Modules.FindDataService("hash/sha256"); if (sha256 && !challenge.empty()) return "AUTH:" + Base64::Encode(sha256->hmac(password, challenge)); if (!challenge.empty() && !sha256) ServerInstance->Logs.Warning(MODNAME, "Not authenticating to server using HMAC-SHA256 because we don't have an SHA256 provider (e.g. the sha2 module) loaded!"); return password; } bool TreeSocket::ComparePass(const Link& link, const std::string& theirs) { capab->auth_fingerprint = !link.Fingerprint.empty(); capab->auth_challenge = !capab->ourchallenge.empty() && !capab->theirchallenge.empty(); const auto* sslhook = SSLIOHook::IsSSL(this); const auto* sslcert = sslhook ? sslhook->GetCertificate() : nullptr; const auto sslcert_usable = sslcert && sslcert->IsUsable(); const auto fp = sslcert_usable ? sslcert->GetFingerprint() : ""; if (capab->auth_fingerprint) { std::string sslerror; if (!sslhook) sslerror = "not using TLS"; if (!sslcert) sslerror = "not using a TLS client certificate"; else if (!sslcert_usable) sslerror = "using an invalid (probably expired) TLS client certificate"; else { std::string badfps; auto foundfp = false; for (const auto& fingerprint : sslcert->GetFingerprints()) { if (InspIRCd::TimingSafeCompare(link.Fingerprint, fingerprint)) { foundfp = true; break; } badfps.append(badfps.empty() ? "" : ", ").append(fingerprint); } if (!foundfp) { sslerror = INSP_FORMAT("not using the correct TLS client certificate (need \"{}\" got \"{}\")", link.Fingerprint, badfps.empty() ? "(none)" : badfps); } } /* Require fingerprint to exist and match */ if (!sslerror.empty()) { ServerInstance->SNO.WriteToSnoMask('l', "Incorrect TLS client certificate on link {}: {}", link.Name, sslerror); SendError("Incorrect TLS client certificate: " + sslerror); return false; } } if (capab->auth_challenge) { std::string our_hmac = MakePass(link.RecvPass, capab->ourchallenge); // Use the timing-safe compare function to compare the hashes if (!InspIRCd::TimingSafeCompare(our_hmac, theirs)) return false; } else { // Use the timing-safe compare function to compare the passwords if (!InspIRCd::TimingSafeCompare(link.RecvPass, theirs)) return false; } // Tell opers to set up fingerprint verification if it's not already set up and the TLS mod gave us a fingerprint // this time if ((!capab->auth_fingerprint) && (!fp.empty())) { ServerInstance->SNO.WriteToSnoMask('l', "TLS client certificate fingerprint for link {} is \"{}\". " "You can improve security by specifying this in .", link.Name, fp); } return true; }