X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fextra%2Fm_ssl_gnutls.cpp;h=e02c4a697d62407c9c20bbddacc7dede404d5002;hb=b4a174ee9c32d62ea6bf010e837e8c5b1c3d36a3;hp=c1ffdfb4c7b53e9f32fb0017aeef4d9236e64392;hpb=fcd538761adad5834c0b6c4c14b43bea9f8bfd6d;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp index c1ffdfb4c..e02c4a697 100644 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@ -1,11 +1,21 @@ /* * InspIRCd -- Internet Relay Chat Daemon * + * Copyright (C) 2020 Matt Schatz + * Copyright (C) 2019 linuxdaemon + * Copyright (C) 2013-2014, 2016-2021 Sadie Powell + * Copyright (C) 2013 Daniel Vassdal + * Copyright (C) 2012-2017 Attila Molnar + * Copyright (C) 2012-2013, 2016 Adam + * Copyright (C) 2012 Robby + * Copyright (C) 2012 ChrisTX * Copyright (C) 2009-2010 Daniel De Graaf - * Copyright (C) 2008 John Brooks - * Copyright (C) 2006-2008 Craig Edwards + * Copyright (C) 2009 Uli Schlachter + * Copyright (C) 2008 Robin Burchell + * Copyright (C) 2008 John Brooks + * Copyright (C) 2007-2008, 2010 Craig Edwards * Copyright (C) 2007 Dennis Friis - * Copyright (C) 2006 Oliver Lupton + * Copyright (C) 2006 Oliver Lupton * * 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 @@ -26,29 +36,41 @@ /// $LinkerFlags: find_linker_flags("gnutls" "-lgnutls") /// $LinkerFlags: require_version("gnutls" "1.0" "2.12") execute("libgcrypt-config --libs" "LIBGCRYPT_LDFLAGS") +/// $PackageInfo: require_system("arch") gnutls pkgconf /// $PackageInfo: require_system("centos") gnutls-devel pkgconfig /// $PackageInfo: require_system("darwin") gnutls pkg-config -/// $PackageInfo: require_system("ubuntu" "1.0" "13.10") libgcrypt11-dev -/// $PackageInfo: require_system("ubuntu" "14.04") gnutls-bin libgnutls-dev pkg-config +/// $PackageInfo: require_system("debian") gnutls-bin libgnutls28-dev pkg-config +/// $PackageInfo: require_system("ubuntu") gnutls-bin libgnutls-dev pkg-config #include "inspircd.h" #include "modules/ssl.h" #include +#ifdef __GNUC__ +# pragma GCC diagnostic push +#endif + // Fix warnings about the use of commas at end of enumerator lists on C++03. #if defined __clang__ # pragma clang diagnostic ignored "-Wc++11-extensions" #elif defined __GNUC__ -# if __GNUC__ < 6 -# pragma GCC diagnostic ignored "-pedantic" +# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) +# pragma GCC diagnostic ignored "-Wpedantic" # else -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# pragma GCC diagnostic ignored "-pedantic" # endif #endif #include #include +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +// Fix warnings about using std::auto_ptr on C++11 or newer. +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #ifndef GNUTLS_VERSION_NUMBER #define GNUTLS_VERSION_NUMBER LIBGNUTLS_VERSION_NUMBER #define GNUTLS_VERSION LIBGNUTLS_VERSION @@ -77,11 +99,6 @@ #define GNUTLS_NEW_PRIO_API #endif -#if (!INSPIRCD_GNUTLS_HAS_VERSION(2, 0, 0)) -typedef gnutls_certificate_credentials_t gnutls_certificate_credentials; -typedef gnutls_dh_params_t gnutls_dh_params; -#endif - enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_HANDSHAKEN }; #if INSPIRCD_GNUTLS_HAS_VERSION(2, 12, 0) @@ -110,10 +127,10 @@ typedef gnutls_connection_end_t inspircd_gnutls_session_init_flags_t; static Module* thismod; -class RandGen : public HandlerBase2 +class RandGen { public: - void Call(char* buffer, size_t len) + static void Call(char* buffer, size_t len) { #ifdef GNUTLS_HAS_RND gnutls_rnd(GNUTLS_RND_RANDOM, buffer, len); @@ -187,14 +204,12 @@ namespace GnuTLS throw Exception("Unknown hash type " + hashname); gnutls_hash_deinit(is_digest, NULL); #else - if (hashname == "md5") + if (stdalgo::string::equalsci(hashname, "md5")) hash = GNUTLS_DIG_MD5; - else if (hashname == "sha1") + else if (stdalgo::string::equalsci(hashname, "sha1")) hash = GNUTLS_DIG_SHA1; -#ifdef INSPIRCD_GNUTLS_ENABLE_SHA256_FINGERPRINT - else if (hashname == "sha256") + else if (stdalgo::string::equalsci(hashname, "sha256")) hash = GNUTLS_DIG_SHA256; -#endif else throw Exception("Unknown hash type " + hashname); #endif @@ -564,7 +579,7 @@ namespace GnuTLS int ret() const { return retval; } }; - class Profile : public refcountbase + class Profile { /** Name of this profile */ @@ -594,22 +609,6 @@ namespace GnuTLS */ const bool requestclientcert; - Profile(const std::string& profilename, const std::string& certstr, const std::string& keystr, - std::auto_ptr& DH, unsigned int mindh, const std::string& hashstr, - const std::string& priostr, std::auto_ptr& CA, std::auto_ptr& CRL, - unsigned int recsize, bool Requestclientcert) - : name(profilename) - , x509cred(certstr, keystr) - , min_dh_bits(mindh) - , hash(hashstr) - , priority(priostr) - , outrecsize(recsize) - , requestclientcert(Requestclientcert) - { - x509cred.SetDH(DH); - x509cred.SetCA(CA, CRL); - } - static std::string ReadFile(const std::string& filename) { FileReader reader(filename); @@ -645,42 +644,66 @@ namespace GnuTLS } public: - static reference Create(const std::string& profilename, ConfigTag* tag) + struct Config { - std::string certstr = ReadFile(tag->getString("certfile", "cert.pem")); - std::string keystr = ReadFile(tag->getString("keyfile", "key.pem")); - - std::auto_ptr dh = DHParams::Import(ReadFile(tag->getString("dhfile", "dhparams.pem"))); - - std::string priostr = GetPrioStr(profilename, tag); - unsigned int mindh = tag->getInt("mindhbits", 1024); - std::string hashstr = tag->getString("hash", "md5"); + std::string name; - // Load trusted CA and revocation list, if set std::auto_ptr ca; std::auto_ptr crl; - std::string filename = tag->getString("cafile"); - if (!filename.empty()) - { - ca.reset(new X509CertList(ReadFile(filename))); - filename = tag->getString("crlfile"); + std::string certstr; + std::string keystr; + std::auto_ptr dh; + + std::string priostr; + unsigned int mindh; + std::string hashstr; + + unsigned int outrecsize; + bool requestclientcert; + + Config(const std::string& profilename, ConfigTag* tag) + : name(profilename) + , certstr(ReadFile(tag->getString("certfile", "cert.pem", 1))) + , keystr(ReadFile(tag->getString("keyfile", "key.pem", 1))) + , dh(DHParams::Import(ReadFile(tag->getString("dhfile", "dhparams.pem", 1)))) + , priostr(GetPrioStr(profilename, tag)) + , mindh(tag->getUInt("mindhbits", 1024)) + , hashstr(tag->getString("hash", "md5", 1)) + , requestclientcert(tag->getBool("requestclientcert", true)) + { + // Load trusted CA and revocation list, if set + std::string filename = tag->getString("cafile"); if (!filename.empty()) - crl.reset(new X509CRL(ReadFile(filename))); - } + { + ca.reset(new X509CertList(ReadFile(filename))); + + filename = tag->getString("crlfile"); + if (!filename.empty()) + crl.reset(new X509CRL(ReadFile(filename))); + } #ifdef INSPIRCD_GNUTLS_HAS_CORK - // If cork support is available outrecsize represents the (rough) max amount of data we give GnuTLS while corked - unsigned int outrecsize = tag->getInt("outrecsize", 2048, 512); + // If cork support is available outrecsize represents the (rough) max amount of data we give GnuTLS while corked + outrecsize = tag->getUInt("outrecsize", 2048, 512); #else - unsigned int outrecsize = tag->getInt("outrecsize", 2048, 512, 16384); + outrecsize = tag->getUInt("outrecsize", 2048, 512, 16384); #endif + } + }; - const bool requestclientcert = tag->getBool("requestclientcert", true); - - return new Profile(profilename, certstr, keystr, dh, mindh, hashstr, priostr, ca, crl, outrecsize, requestclientcert); + Profile(Config& config) + : name(config.name) + , x509cred(config.certstr, config.keystr) + , min_dh_bits(config.mindh) + , hash(config.hashstr) + , priority(config.priostr) + , outrecsize(config.outrecsize) + , requestclientcert(config.requestclientcert) + { + x509cred.SetDH(config.dh); + x509cred.SetCA(config.ca, config.crl); } - /** Set up the given session with the settings in this profile */ void SetupSession(gnutls_session_t sess) @@ -706,7 +729,6 @@ class GnuTLSIOHook : public SSLIOHook private: gnutls_session_t sess; issl_status status; - reference profile; #ifdef INSPIRCD_GNUTLS_HAS_CORK size_t gbuffersize; #endif @@ -853,7 +875,7 @@ class GnuTLSIOHook : public SSLIOHook issuer.clear(); } - if ((ret = gnutls_x509_crt_get_fingerprint(cert, profile->GetHash(), digest, &digest_size)) < 0) + if ((ret = gnutls_x509_crt_get_fingerprint(cert, GetProfile().GetHash(), digest, &digest_size)) < 0) { certinfo->error = gnutls_strerror(ret); } @@ -885,7 +907,7 @@ info_done_dealloc: } CloseSession(); - sock->SetError("No SSL session"); + sock->SetError("No TLS (SSL) session"); return -1; } @@ -1041,11 +1063,10 @@ info_done_dealloc: #endif // INSPIRCD_GNUTLS_HAS_VECTOR_PUSH public: - GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, inspircd_gnutls_session_init_flags_t flags, const reference& sslprofile) + GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, inspircd_gnutls_session_init_flags_t flags) : SSLIOHook(hookprov) , sess(NULL) , status(ISSL_NONE) - , profile(sslprofile) #ifdef INSPIRCD_GNUTLS_HAS_CORK , gbuffersize(0) #endif @@ -1058,7 +1079,7 @@ info_done_dealloc: gnutls_transport_set_push_function(sess, gnutls_push_wrapper); #endif gnutls_transport_set_pull_function(sess, gnutls_pull_wrapper); - profile->SetupSession(sess); + GetProfile().SetupSession(sess); sock->AddIOHook(this); Handshake(sock); @@ -1130,7 +1151,7 @@ info_done_dealloc: // GnuTLS buffer is empty but sendq is not, begin sending data from the sendq gnutls_record_cork(this->sess); - while ((!sendq.empty()) && (gbuffersize < profile->GetOutgoingRecordSize())) + while ((!sendq.empty()) && (gbuffersize < GetProfile().GetOutgoingRecordSize())) { const StreamSocket::SendQueue::Element& elem = sendq.front(); gbuffersize += elem.length(); @@ -1148,7 +1169,7 @@ info_done_dealloc: while (!sendq.empty()) { - FlattenSendQueue(sendq, profile->GetOutgoingRecordSize()); + FlattenSendQueue(sendq, GetProfile().GetOutgoingRecordSize()); const StreamSocket::SendQueue::Element& buffer = sendq.front(); ret = HandleWriteRet(user, gnutls_record_send(this->sess, buffer.data(), buffer.length())); @@ -1180,7 +1201,26 @@ info_done_dealloc: out.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess)))); } - GnuTLS::Profile* GetProfile() { return profile; } + bool GetServerName(std::string& out) const CXX11_OVERRIDE + { + std::vector nameBuffer; + size_t nameLength = 0; + unsigned int nameType = GNUTLS_NAME_DNS; + + // First, determine the size of the hostname. + if (gnutls_server_name_get(sess, &nameBuffer[0], &nameLength, &nameType, 0) != GNUTLS_E_SHORT_MEMORY_BUFFER) + return false; + + // Then retrieve the hostname. + nameBuffer.resize(nameLength); + if (gnutls_server_name_get(sess, &nameBuffer[0], &nameLength, &nameType, 0) != GNUTLS_E_SUCCESS) + return false; + + out.append(&nameBuffer[0]); + return true; + } + + GnuTLS::Profile& GetProfile(); bool IsHandshakeDone() const { return (status == ISSL_HANDSHAKEN); } }; @@ -1193,7 +1233,7 @@ int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_d st->key_type = GNUTLS_PRIVKEY_X509; #endif StreamSocket* sock = reinterpret_cast(gnutls_transport_get_ptr(sess)); - GnuTLS::X509Credentials& cred = static_cast(sock->GetModHook(thismod))->GetProfile()->GetX509Credentials(); + GnuTLS::X509Credentials& cred = static_cast(sock->GetModHook(thismod))->GetProfile().GetX509Credentials(); st->ncerts = cred.certs.size(); st->cert.x509 = cred.certs.raw(); @@ -1203,14 +1243,14 @@ int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_d return 0; } -class GnuTLSIOHookProvider : public refcountbase, public IOHookProvider +class GnuTLSIOHookProvider : public SSLIOHookProvider { - reference profile; + GnuTLS::Profile profile; public: - GnuTLSIOHookProvider(Module* mod, reference& prof) - : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL) - , profile(prof) + GnuTLSIOHookProvider(Module* mod, GnuTLS::Profile::Config& config) + : SSLIOHookProvider(mod, config.name) + , profile(config) { ServerInstance->Modules->AddService(*this); } @@ -1222,29 +1262,36 @@ class GnuTLSIOHookProvider : public refcountbase, public IOHookProvider void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE { - new GnuTLSIOHook(this, sock, GNUTLS_SERVER, profile); + new GnuTLSIOHook(this, sock, GNUTLS_SERVER); } void OnConnect(StreamSocket* sock) CXX11_OVERRIDE { - new GnuTLSIOHook(this, sock, GNUTLS_CLIENT, profile); + new GnuTLSIOHook(this, sock, GNUTLS_CLIENT); } + + GnuTLS::Profile& GetProfile() { return profile; } }; +GnuTLS::Profile& GnuTLSIOHook::GetProfile() +{ + IOHookProvider* hookprov = prov; + return static_cast(hookprov)->GetProfile(); +} + class ModuleSSLGnuTLS : public Module { typedef std::vector > ProfileList; // First member of the class, gets constructed first and destructed last GnuTLS::Init libinit; - RandGen randhandler; ProfileList profiles; void ReadProfiles() { // First, store all profiles in a new, temporary container. If no problems occur, swap the two // containers; this way if something goes wrong we can go back and continue using the current profiles, - // avoiding unpleasant situations where no new SSL connections are possible. + // avoiding unpleasant situations where no new TLS (SSL) connections are possible. ProfileList newprofiles; ConfigTagList tags = ServerInstance->Config->ConfTags("sslprofile"); @@ -1253,47 +1300,60 @@ class ModuleSSLGnuTLS : public Module // No tags found, create a profile named "gnutls" from settings in the block const std::string defname = "gnutls"; ConfigTag* tag = ServerInstance->Config->ConfValue(defname); - ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No tags found; using settings from the tag"); + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "No tags found; using settings from the deprecated tag"); try { - reference profile(GnuTLS::Profile::Create(defname, tag)); - newprofiles.push_back(new GnuTLSIOHookProvider(this, profile)); + GnuTLS::Profile::Config profileconfig(defname, tag); + newprofiles.push_back(new GnuTLSIOHookProvider(this, profileconfig)); } catch (CoreException& ex) { - throw ModuleException("Error while initializing the default SSL profile - " + ex.GetReason()); + throw ModuleException("Error while initializing the default TLS (SSL) profile - " + ex.GetReason()); } } - - for (ConfigIter i = tags.first; i != tags.second; ++i) + else { - ConfigTag* tag = i->second; - if (tag->getString("provider") != "gnutls") - continue; - - std::string name = tag->getString("name"); - if (name.empty()) + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "You have defined an tag; you should use this in place of \"gnutls\" when configuring TLS (SSL) connections in or "); + for (ConfigIter i = tags.first; i != tags.second; ++i) { - ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring tag without name at " + tag->getTagLocation()); - continue; - } + ConfigTag* tag = i->second; + if (!stdalgo::string::equalsci(tag->getString("provider"), "gnutls")) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring non-GnuTLS tag at " + tag->getTagLocation()); + continue; + } - reference profile; - try - { - profile = GnuTLS::Profile::Create(name, tag); - } - catch (CoreException& ex) - { - throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason()); - } + std::string name = tag->getString("name"); + if (name.empty()) + { + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring tag without name at " + tag->getTagLocation()); + continue; + } + + reference prov; + try + { + GnuTLS::Profile::Config profileconfig(name, tag); + prov = new GnuTLSIOHookProvider(this, profileconfig); + } + catch (CoreException& ex) + { + throw ModuleException("Error while initializing TLS (SSL) profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason()); + } - newprofiles.push_back(new GnuTLSIOHookProvider(this, profile)); + newprofiles.push_back(prov); + } } // New profiles are ok, begin using them // Old profiles are deleted when their refcount drops to zero + for (ProfileList::iterator i = profiles.begin(); i != profiles.end(); ++i) + { + GnuTLSIOHookProvider& prov = **i; + ServerInstance->Modules.DelService(prov); + } + profiles.swap(newprofiles); } @@ -1310,17 +1370,18 @@ class ModuleSSLGnuTLS : public Module { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "GnuTLS lib version %s module was compiled for " GNUTLS_VERSION, gnutls_check_version(NULL)); ReadProfiles(); - ServerInstance->GenRandom = &randhandler; + ServerInstance->GenRandom = RandGen::Call; } void OnModuleRehash(User* user, const std::string ¶m) CXX11_OVERRIDE { - if(param != "ssl") + if (!irc::equals(param, "tls") && !irc::equals(param, "ssl")) return; try { ReadProfiles(); + ServerInstance->SNO->WriteToSnoMask('a', "GnuTLS TLS (SSL) profiles have been reloaded."); } catch (ModuleException& ex) { @@ -1330,27 +1391,27 @@ class ModuleSSLGnuTLS : public Module ~ModuleSSLGnuTLS() { - ServerInstance->GenRandom = &ServerInstance->HandleGenRandom; + ServerInstance->GenRandom = &InspIRCd::DefaultGenRandom; } - void OnCleanup(int target_type, void* item) CXX11_OVERRIDE + void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE { - if(target_type == TYPE_USER) + if (type == ExtensionItem::EXT_USER) { LocalUser* user = IS_LOCAL(static_cast(item)); if ((user) && (user->eh.GetModHook(this))) { - // User is using SSL, they're a local user, and they're using one of *our* SSL ports. - // Potentially there could be multiple SSL modules loaded at once on different ports. - ServerInstance->Users->QuitUser(user, "SSL module unloading"); + // User is using TLS (SSL), they're a local user, and they're using one of *our* TLS (SSL) ports. + // Potentially there could be multiple TLS (SSL) modules loaded at once on different ports. + ServerInstance->Users->QuitUser(user, "GnuTLS module unloading"); } } } Version GetVersion() CXX11_OVERRIDE { - return Version("Provides SSL support for clients", VF_VENDOR); + return Version("Allows TLS (SSL) encrypted connections using the GnuTLS library.", VF_VENDOR); } ModResult OnCheckReady(LocalUser* user) CXX11_OVERRIDE