diff options
| author | lethality <lee@lethality.me.uk> | 2014-02-18 22:24:24 +0000 |
|---|---|---|
| committer | lethality <lee@lethality.me.uk> | 2014-02-18 22:24:24 +0000 |
| commit | 5933a5e9b1ade9f926145b67e557b87ae6dd6e8f (patch) | |
| tree | 41c9c8b4c1818531251c292ce184601a8721aed4 /modules | |
| parent | f1c5c27f3fda90265d5da8205db1bdd4d12c9851 (diff) | |
| parent | 109d8f431f3d25d56570a6aaa1af957867bbb80e (diff) | |
Merge remote branch 'upstream/2.0' into 2.0
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/commands/os_forbid.cpp | 54 | ||||
| -rw-r--r-- | modules/commands/os_ignore.cpp | 160 | ||||
| -rw-r--r-- | modules/commands/os_session.cpp | 2 | ||||
| -rw-r--r-- | modules/database/db_old.cpp | 24 | ||||
| -rw-r--r-- | modules/encryption/enc_md5.cpp | 2 | ||||
| -rw-r--r-- | modules/encryption/enc_old.cpp | 2 | ||||
| -rw-r--r-- | modules/encryption/enc_sha1.cpp | 2 | ||||
| -rw-r--r-- | modules/encryption/enc_sha256.cpp | 2 | ||||
| -rw-r--r-- | modules/encryption/encryption.h | 37 | ||||
| -rw-r--r-- | modules/extra/enc_bcrypt.cpp | 2 | ||||
| -rw-r--r-- | modules/extra/m_ssl_gnutls.cpp | 509 | ||||
| -rw-r--r-- | modules/extra/m_ssl_openssl.cpp (renamed from modules/extra/m_ssl.cpp) | 4 | ||||
| -rw-r--r-- | modules/m_httpd.cpp | 3 | ||||
| -rw-r--r-- | modules/webcpanel/template_fileserver.cpp | 11 |
14 files changed, 699 insertions, 115 deletions
diff --git a/modules/commands/os_forbid.cpp b/modules/commands/os_forbid.cpp index c1fe1af8b..11215ab05 100644 --- a/modules/commands/os_forbid.cpp +++ b/modules/commands/os_forbid.cpp @@ -14,6 +14,51 @@ static ServiceReference<NickServService> nickserv("NickServService", "NickServ"); +struct ForbidDataImpl : ForbidData, Serializable +{ + ForbidDataImpl() : Serializable("ForbidData") { } + void Serialize(Serialize::Data &data) const anope_override; + static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); +}; + +void ForbidDataImpl::Serialize(Serialize::Data &data) const +{ + data["mask"] << this->mask; + data["creator"] << this->creator; + data["reason"] << this->reason; + data["created"] << this->created; + data["expires"] << this->expires; + data["type"] << this->type; +} + +Serializable* ForbidDataImpl::Unserialize(Serializable *obj, Serialize::Data &data) +{ + if (!forbid_service) + return NULL; + + ForbidDataImpl *fb; + if (obj) + fb = anope_dynamic_static_cast<ForbidDataImpl *>(obj); + else + fb = new ForbidDataImpl(); + + data["mask"] >> fb->mask; + data["creator"] >> fb->creator; + data["reason"] >> fb->reason; + data["created"] >> fb->created; + data["expires"] >> fb->expires; + unsigned int t; + data["type"] >> t; + fb->type = static_cast<ForbidType>(t); + + if (t > FT_SIZE - 1) + return NULL; + + if (!obj) + forbid_service->AddForbid(fb); + return fb; +} + class MyForbidService : public ForbidService { Serialize::Checker<std::vector<ForbidData *>[FT_SIZE - 1]> forbid_data; @@ -43,6 +88,11 @@ class MyForbidService : public ForbidService delete d; } + ForbidData *CreateForbid() anope_override + { + return new ForbidDataImpl(); + } + ForbidData *FindForbid(const Anope::string &mask, ForbidType ftype) anope_override { for (unsigned i = this->forbids(ftype).size(); i > 0; --i) @@ -157,7 +207,7 @@ class CommandOSForbid : public Command bool created = false; if (d == NULL) { - d = new ForbidData(); + d = new ForbidDataImpl(); created = true; } @@ -379,7 +429,7 @@ class OSForbid : public Module public: OSForbid(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - forbidService(this), forbiddata_type("ForbidData", ForbidData::Unserialize), commandosforbid(this) + forbidService(this), forbiddata_type("ForbidData", ForbidDataImpl::Unserialize), commandosforbid(this) { } diff --git a/modules/commands/os_ignore.cpp b/modules/commands/os_ignore.cpp index b4d4b09a3..e00b4dd31 100644 --- a/modules/commands/os_ignore.cpp +++ b/modules/commands/os_ignore.cpp @@ -12,61 +12,94 @@ #include "module.h" #include "modules/os_ignore.h" +struct IgnoreDataImpl : IgnoreData, Serializable +{ + IgnoreDataImpl() : Serializable("IgnoreData") { } + ~IgnoreDataImpl(); + void Serialize(Serialize::Data &data) const anope_override; + static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); +}; + +IgnoreDataImpl::~IgnoreDataImpl() +{ + if (ignore_service) + ignore_service->DelIgnore(this); +} + +void IgnoreDataImpl::Serialize(Serialize::Data &data) const +{ + data["mask"] << this->mask; + data["creator"] << this->creator; + data["reason"] << this->reason; + data["time"] << this->time; +} + +Serializable* IgnoreDataImpl::Unserialize(Serializable *obj, Serialize::Data &data) +{ + if (!ignore_service) + return NULL; + + IgnoreDataImpl *ign; + if (obj) + ign = anope_dynamic_static_cast<IgnoreDataImpl *>(obj); + else + { + ign = new IgnoreDataImpl(); + ignore_service->AddIgnore(ign); + } + + data["mask"] >> ign->mask; + data["creator"] >> ign->creator; + data["reason"] >> ign->reason; + data["time"] >> ign->time; + + return ign; +} + + class OSIgnoreService : public IgnoreService { + Serialize::Checker<std::vector<IgnoreData *> > ignores; + public: - OSIgnoreService(Module *o) : IgnoreService(o) { } + OSIgnoreService(Module *o) : IgnoreService(o), ignores("IgnoreData") { } - IgnoreData* AddIgnore(const Anope::string &mask, const Anope::string &creator, const Anope::string &reason, time_t delta = Anope::CurTime) anope_override + void AddIgnore(IgnoreData *ign) anope_override { - /* Check if we already got an identical entry. */ - IgnoreData *ign = this->Find(mask); - if (ign != NULL) - { - if (!delta) - ign->time = 0; - else - ign->time = Anope::CurTime + delta; - return ign; - } - /* Create new entry.. */ - else - { - IgnoreData newign; - newign.mask = mask; - newign.creator = creator; - newign.reason = reason; - newign.time = delta ? Anope::CurTime + delta : 0; - this->ignores.push_back(newign); - return &this->ignores.back(); - } + ignores->push_back(ign); } - bool DelIgnore(const Anope::string &mask) anope_override + void DelIgnore(IgnoreData *ign) anope_override { - for (std::list<IgnoreData>::iterator it = this->ignores.begin(), it_end = this->ignores.end(); it != it_end; ++it) + std::vector<IgnoreData *>::iterator it = std::find(ignores->begin(), ignores->end(), ign); + if (it != ignores->end()) + ignores->erase(it); + } + + void ClearIgnores() anope_override + { + for (unsigned i = ignores->size(); i > 0; --i) { - IgnoreData &idn = *it; - if (idn.mask.equals_ci(mask)) - { - this->ignores.erase(it); - return true; - } + IgnoreData *ign = ignores->at(i - 1); + delete ign; } + } - return false; + IgnoreData *Create() anope_override + { + return new IgnoreDataImpl(); } IgnoreData *Find(const Anope::string &mask) anope_override { User *u = User::Find(mask, true); - std::list<IgnoreData>::iterator ign = this->ignores.begin(), ign_end = this->ignores.end(); + std::vector<IgnoreData *>::iterator ign = this->ignores->begin(), ign_end = this->ignores->end(); if (u) { for (; ign != ign_end; ++ign) { - Entry ignore_mask("", ign->mask); + Entry ignore_mask("", (*ign)->mask); if (ignore_mask.Matches(u, true)) break; } @@ -94,26 +127,31 @@ class OSIgnoreService : public IgnoreService tmp = mask + "!*@*"; for (; ign != ign_end; ++ign) - if (Anope::Match(tmp, ign->mask, false, true)) + if (Anope::Match(tmp, (*ign)->mask, false, true)) break; } /* Check whether the entry has timed out */ if (ign != ign_end) { - IgnoreData &id = *ign; + IgnoreData *id = *ign; - if (id.time && !Anope::NoExpire && id.time <= Anope::CurTime) + if (id->time && !Anope::NoExpire && id->time <= Anope::CurTime) { - Log(LOG_NORMAL, "expire/ignore", Config->GetClient("OperServ")) << "Expiring ignore entry " << id.mask; - this->ignores.erase(ign); + Log(LOG_NORMAL, "expire/ignore", Config->GetClient("OperServ")) << "Expiring ignore entry " << id->mask; + delete id; } else - return &id; + return id; } return NULL; } + + std::vector<IgnoreData *> &GetIgnores() anope_override + { + return *ignores; + } }; class CommandOSIgnore : public Command @@ -183,7 +221,13 @@ class CommandOSIgnore : public Command if (Anope::ReadOnly) source.Reply(READ_ONLY_MODE); - ignore_service->AddIgnore(mask, source.GetNick(), reason, t); + IgnoreData *ign = new IgnoreDataImpl(); + ign->mask = mask; + ign->creator = source.GetNick(); + ign->reason = reason; + ign->time = t ? Anope::CurTime + t : 0; + + ignore_service->AddIgnore(ign); if (!t) { source.Reply(_("\002%s\002 will now permanently be ignored."), mask.c_str()); @@ -202,18 +246,15 @@ class CommandOSIgnore : public Command if (!ignore_service) return; - std::list<IgnoreData> &ignores = ignore_service->GetIgnores(); - - for (std::list<IgnoreData>::iterator it = ignores.begin(), next_it; it != ignores.end(); it = next_it) + std::vector<IgnoreData *> &ignores = ignore_service->GetIgnores(); + for (unsigned i = ignores.size(); i > 0; --i) { - IgnoreData &id = *it; - next_it = it; - ++next_it; + IgnoreData *id = ignores[i - 1]; - if (id.time && !Anope::NoExpire && id.time <= Anope::CurTime) + if (id->time && !Anope::NoExpire && id->time <= Anope::CurTime) { - Log(LOG_NORMAL, "expire/ignore", Config->GetClient("OperServ")) << "Expiring ignore entry " << id.mask; - ignores.erase(it); + Log(LOG_NORMAL, "expire/ignore", Config->GetClient("OperServ")) << "Expiring ignore entry " << id->mask; + delete id; } } @@ -223,15 +264,16 @@ class CommandOSIgnore : public Command { ListFormatter list(source.GetAccount()); list.AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Reason")).AddColumn(_("Expires")); - for (std::list<IgnoreData>::const_iterator ign = ignores.begin(), ign_end = ignores.end(); ign != ign_end; ++ign) + + for (unsigned i = ignores.size(); i > 0; --i) { - const IgnoreData &ignore = *ign; + const IgnoreData *ignore = ignores[i - 1]; ListFormatter::ListEntry entry; - entry["Mask"] = ignore.mask; - entry["Creator"] = ignore.creator; - entry["Reason"] = ignore.reason; - entry["Expires"] = Anope::Expires(ignore.time, source.GetAccount()); + entry["Mask"] = ignore->mask; + entry["Creator"] = ignore->creator; + entry["Reason"] = ignore->reason; + entry["Expires"] = Anope::Expires(ignore->time, source.GetAccount()); list.AddEntry(entry); } @@ -264,13 +306,15 @@ class CommandOSIgnore : public Command return; } - if (ignore_service->DelIgnore(mask)) + IgnoreData *ign = ignore_service->Find(mask); + if (ign) { if (Anope::ReadOnly) source.Reply(READ_ONLY_MODE); Log(LOG_ADMIN, source, this) << "to remove an ignore on " << mask; source.Reply(_("\002%s\002 will no longer be ignored."), mask.c_str()); + delete ign; } else source.Reply(_("\002%s\002 not found on ignore list."), mask.c_str()); @@ -355,7 +399,7 @@ class OSIgnore : public Module public: OSIgnore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - ignoredata_type("IgnoreData", IgnoreData::Unserialize), osignoreservice(this), commandosignore(this) + ignoredata_type("IgnoreData", IgnoreDataImpl::Unserialize), osignoreservice(this), commandosignore(this) { } diff --git a/modules/commands/os_session.cpp b/modules/commands/os_session.cpp index 8f5abdc1d..d3e82b04a 100644 --- a/modules/commands/os_session.cpp +++ b/modules/commands/os_session.cpp @@ -668,7 +668,7 @@ class OSSession : public Module if (u->Quitting() || !session_limit || exempt || !u->server || u->server->IsULined()) return; - cidr u_ip(u->ip); + cidr u_ip(u->ip, u->ip.find(':') != Anope::string::npos ? ipv6_cidr : ipv4_cidr); if (!u_ip.valid()) return; diff --git a/modules/database/db_old.cpp b/modules/database/db_old.cpp index 8c322d02e..8fe57cfdd 100644 --- a/modules/database/db_old.cpp +++ b/modules/database/db_old.cpp @@ -620,9 +620,18 @@ static void LoadNicks() if (tmpu16 & OLD_NS_VERBOTEN) { if (!forbid) + { + delete nc; + continue; + } + + if (nc->display.find_first_of("?*") != Anope::string::npos) + { + delete nc; continue; + } - ForbidData *d = new ForbidData(); + ForbidData *d = forbid->CreateForbid(); d->mask = nc->display; d->creator = last_usermask; d->reason = last_realname; @@ -783,7 +792,7 @@ static void LoadChannels() if (tmpu32 & OLD_CI_SECURE) ci->Extend<bool>("CS_SECURE"); if (tmpu32 & OLD_CI_NO_EXPIRE) - ci->Extend<bool>("CI_NO_EXPIRE"); + ci->Extend<bool>("CS_NO_EXPIRE"); if (tmpu32 & OLD_CI_MEMO_HARDMAX) ci->Extend<bool>("MEMO_HARDMAX"); if (tmpu32 & OLD_CI_SECUREFOUNDER) @@ -1029,9 +1038,18 @@ static void LoadChannels() if (forbid_chan) { if (!forbid) + { + delete ci; + continue; + } + + if (ci->name.find_first_of("?*") != Anope::string::npos) + { + delete ci; continue; + } - ForbidData *d = new ForbidData(); + ForbidData *d = forbid->CreateForbid(); d->mask = ci->name; d->creator = forbidby; d->reason = forbidreason; diff --git a/modules/encryption/enc_md5.cpp b/modules/encryption/enc_md5.cpp index ae51e0394..b56f03324 100644 --- a/modules/encryption/enc_md5.cpp +++ b/modules/encryption/enc_md5.cpp @@ -12,7 +12,7 @@ */ #include "module.h" -#include "encryption.h" +#include "modules/encryption.h" /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. diff --git a/modules/encryption/enc_old.cpp b/modules/encryption/enc_old.cpp index e56224e22..c8c1093da 100644 --- a/modules/encryption/enc_old.cpp +++ b/modules/encryption/enc_old.cpp @@ -10,7 +10,7 @@ */ #include "module.h" -#include "encryption.h" +#include "modules/encryption.h" static ServiceReference<Encryption::Provider> md5("Encryption::Provider", "md5"); diff --git a/modules/encryption/enc_sha1.cpp b/modules/encryption/enc_sha1.cpp index 4ef3e51fe..38f32b2cb 100644 --- a/modules/encryption/enc_sha1.cpp +++ b/modules/encryption/enc_sha1.cpp @@ -15,7 +15,7 @@ A million repetitions of "a" /* #define LITTLE_ENDIAN * This should be #define'd if true. */ #include "module.h" -#include "encryption.h" +#include "modules/encryption.h" union CHAR64LONG16 { diff --git a/modules/encryption/enc_sha256.cpp b/modules/encryption/enc_sha256.cpp index 7533c2891..cccbceea1 100644 --- a/modules/encryption/enc_sha256.cpp +++ b/modules/encryption/enc_sha256.cpp @@ -48,7 +48,7 @@ */ #include "module.h" -#include "encryption.h" +#include "modules/encryption.h" static const unsigned SHA256_DIGEST_SIZE = 256 / 8; static const unsigned SHA256_BLOCK_SIZE = 512 / 8; diff --git a/modules/encryption/encryption.h b/modules/encryption/encryption.h deleted file mode 100644 index 95c5703aa..000000000 --- a/modules/encryption/encryption.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * (C) 2003-2014 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - * - * Based on the original code of Epona by Lara. - * Based on the original code of Services by Andy Church. - * - */ - -namespace Encryption -{ - typedef std::pair<const unsigned char *, size_t> Hash; - typedef std::pair<const uint32_t *, size_t> IV; - - class Context - { - public: - virtual ~Context() { } - virtual void Update(const unsigned char *data, size_t len) = 0; - virtual void Finalize() = 0; - virtual Hash GetFinalizedHash() = 0; - }; - - class Provider : public Service - { - public: - Provider(Module *creator, const Anope::string &sname) : Service(creator, "Encryption::Provider", sname) { } - virtual ~Provider() { } - - virtual Context *CreateContext(IV * = NULL) = 0; - virtual IV GetDefaultIV() = 0; - }; -} - diff --git a/modules/extra/enc_bcrypt.cpp b/modules/extra/enc_bcrypt.cpp index 67f2e8bef..cd5e05c35 100644 --- a/modules/extra/enc_bcrypt.cpp +++ b/modules/extra/enc_bcrypt.cpp @@ -1,7 +1,7 @@ /* RequiredLibraries: xcrypt */ #include "module.h" -#include "encryption.h" +#include "modules/encryption.h" #include <xcrypt.h> class EBCRYPT : public Module diff --git a/modules/extra/m_ssl_gnutls.cpp b/modules/extra/m_ssl_gnutls.cpp new file mode 100644 index 000000000..002015f5f --- /dev/null +++ b/modules/extra/m_ssl_gnutls.cpp @@ -0,0 +1,509 @@ +/* + * (C) 2014 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + */ + +/* RequiredLibraries: gnutls */ + +#include "module.h" +#include "modules/ssl.h" + +#include <errno.h> +#include <gnutls/gnutls.h> + +class GnuTLSModule; +static GnuTLSModule *me; + +class MySSLService : public SSLService +{ + public: + MySSLService(Module *o, const Anope::string &n); + + /** Initialize a socket to use SSL + * @param s The socket + */ + void Init(Socket *s) anope_override; +}; + +class SSLSocketIO : public SocketIO +{ + public: + gnutls_session_t sess; + + /** Constructor + */ + SSLSocketIO() : sess(NULL) { } + + /** Really receive something from the buffer + * @param s The socket + * @param buf The buf to read to + * @param sz How much to read + * @return Number of bytes received + */ + int Recv(Socket *s, char *buf, size_t sz) anope_override; + + /** Write something to the socket + * @param s The socket + * @param buf The data to write + * @param size The length of the data + */ + int Send(Socket *s, const char *buf, size_t sz) anope_override; + + /** Accept a connection from a socket + * @param s The socket + * @return The new socket + */ + ClientSocket *Accept(ListenSocket *s) anope_override; + + /** Finished accepting a connection from a socket + * @param s The socket + * @return SF_ACCEPTED if accepted, SF_ACCEPTING if still in process, SF_DEAD on error + */ + SocketFlag FinishAccept(ClientSocket *cs) anope_override; + + /** Connect the socket + * @param s THe socket + * @param target IP to connect to + * @param port to connect to + */ + void Connect(ConnectionSocket *s, const Anope::string &target, int port) anope_override; + + /** Called to potentially finish a pending connection + * @param s The socket + * @return SF_CONNECTED on success, SF_CONNECTING if still pending, and SF_DEAD on error. + */ + SocketFlag FinishConnect(ConnectionSocket *s) anope_override; + + /** Called when the socket is destructing + */ + void Destroy() anope_override; +}; + +namespace GnuTLS +{ + class Init + { + public: + Init() { gnutls_global_init(); } + ~Init() { gnutls_global_deinit(); } + }; + + /** Used to create a gnutls_datum_t* from an Anope::string + */ + class Datum + { + gnutls_datum_t datum; + + public: + Datum(const Anope::string &dat) + { + datum.data = reinterpret_cast<unsigned char *>(const_cast<char *>(dat.data())); + datum.size = static_cast<unsigned int>(dat.length()); + } + + const gnutls_datum_t *get() const { return &datum; } + }; + + class DHParams + { + gnutls_dh_params_t dh_params; + + public: + DHParams() : dh_params(NULL) { } + + void Import(const Anope::string &dhstr) + { + if (dh_params != NULL) + { + gnutls_dh_params_deinit(dh_params); + dh_params = NULL; + } + + int ret = gnutls_dh_params_init(&dh_params); + if (ret < 0) + throw ConfigException("Unable to initialize DH parameters"); + + ret = gnutls_dh_params_import_pkcs3(dh_params, Datum(dhstr).get(), GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + gnutls_dh_params_deinit(dh_params); + dh_params = NULL; + throw ConfigException("Unable to import DH parameters"); + } + } + + ~DHParams() + { + if (dh_params) + gnutls_dh_params_deinit(dh_params); + } + + gnutls_dh_params_t get() const { return dh_params; } + }; + + class X509CertCredentials + { + gnutls_certificate_credentials_t cred; + DHParams dh; + + public: + X509CertCredentials() + { + if (gnutls_certificate_allocate_credentials(&cred) < 0) + throw ConfigException("Cannot allocate certificate credentials"); + } + + ~X509CertCredentials() + { + gnutls_certificate_free_credentials(cred); + } + + void SetupSession(gnutls_session_t sess) + { + gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, cred); + gnutls_set_default_priority(sess); + } + + void SetCertAndKey(const Anope::string &certfile, const Anope::string &keyfile) + { + int ret = gnutls_certificate_set_x509_key_file(cred, certfile.c_str(), keyfile.c_str(), GNUTLS_X509_FMT_PEM); + if (ret < 0) + throw ConfigException("Unable to load certificate/private key: " + Anope::string(gnutls_strerror(ret))); + } + + void SetDH(const Anope::string &dhfile) + { + std::ifstream ifs(dhfile.c_str()); + const Anope::string dhdata((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()); + + dh.Import(dhdata); + gnutls_certificate_set_dh_params(cred, dh.get()); + } + + bool HasDH() const + { + return (dh.get() != NULL); + } + }; +} + +class GnuTLSModule : public Module +{ + GnuTLS::Init libinit; + + public: + GnuTLS::X509CertCredentials cred; + MySSLService service; + + GnuTLSModule(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), service(this, "ssl") + { + me = this; + this->SetPermanent(true); + } + + ~GnuTLSModule() + { + for (std::map<int, Socket *>::const_iterator it = SocketEngine::Sockets.begin(), it_end = SocketEngine::Sockets.end(); it != it_end;) + { + Socket *s = it->second; + ++it; + + if (dynamic_cast<SSLSocketIO *>(s->io)) + delete s; + } + } + + static void CheckFile(const Anope::string &filename) + { + if (!Anope::IsFile(filename.c_str())) + { + Log() << "File does not exist: " << filename; + throw ConfigException("Error loading certificate/private key"); + } + } + + void OnReload(Configuration::Conf *conf) anope_override + { + Configuration::Block *config = conf->GetModule(this); + + const Anope::string certfile = config->Get<const Anope::string>("cert", "data/anope.crt"); + const Anope::string keyfile = config->Get<const Anope::string>("key", "data/anope.key"); + const Anope::string dhfile = config->Get<const Anope::string>("dh", "data/dhparams.pem"); + + CheckFile(certfile); + CheckFile(keyfile); + + // DH params is not mandatory + if (Anope::IsFile(dhfile.c_str())) + { + cred.SetDH(dhfile); + Log(LOG_DEBUG) << "m_ssl_gnutls: Successfully loaded DH parameters from " << dhfile; + } + + cred.SetCertAndKey(certfile, keyfile); + Log(LOG_DEBUG) << "m_ssl_gnutls: Successfully loaded certificate " << certfile << " and private key " << keyfile; + } + + void OnPreServerConnect() anope_override + { + Configuration::Block *config = Config->GetBlock("uplink", Anope::CurrentUplink); + + if (config->Get<bool>("ssl")) + { + this->service.Init(UplinkSock); + } + } +}; + +MySSLService::MySSLService(Module *o, const Anope::string &n) : SSLService(o, n) +{ +} + +void MySSLService::Init(Socket *s) +{ + if (s->io != &NormalSocketIO) + throw CoreException("Socket initializing SSL twice"); + + s->io = new SSLSocketIO(); +} + +int SSLSocketIO::Recv(Socket *s, char *buf, size_t sz) +{ + int ret = gnutls_record_recv(this->sess, buf, sz); + + if (ret > 0) + TotalRead += ret; + else if (ret < 0) + { + switch (ret) + { + case GNUTLS_E_AGAIN: + case GNUTLS_E_INTERRUPTED: + SocketEngine::SetLastError(EAGAIN); + break; + default: + if (s == UplinkSock) + { + // Log and fake an errno because this is a fatal error on the uplink socket + Log() << "SSL error: " << gnutls_strerror(ret); + } + SocketEngine::SetLastError(ECONNRESET); + } + } + + return ret; +} + +int SSLSocketIO::Send(Socket *s, const char *buf, size_t sz) +{ + int ret = gnutls_record_send(this->sess, buf, sz); + + if (ret > 0) + TotalWritten += ret; + else + { + switch (ret) + { + case 0: + case GNUTLS_E_AGAIN: + case GNUTLS_E_INTERRUPTED: + SocketEngine::SetLastError(EAGAIN); + break; + default: + if (s == UplinkSock) + { + // Log and fake an errno because this is a fatal error on the uplink socket + Log() << "SSL error: " << gnutls_strerror(ret); + } + SocketEngine::SetLastError(ECONNRESET); + } + } + + return ret; +} + +ClientSocket *SSLSocketIO::Accept(ListenSocket *s) +{ + if (s->io == &NormalSocketIO) + throw SocketException("Attempting to accept on uninitialized socket with SSL"); + + sockaddrs conaddr; + + socklen_t size = sizeof(conaddr); + int newsock = accept(s->GetFD(), &conaddr.sa, &size); + +#ifndef INVALID_SOCKET + const int INVALID_SOCKET = -1; +#endif + + if (newsock < 0 || newsock == INVALID_SOCKET) + throw SocketException("Unable to accept connection: " + Anope::LastError()); + + ClientSocket *newsocket = s->OnAccept(newsock, conaddr); + me->service.Init(newsocket); + SSLSocketIO *io = anope_dynamic_static_cast<SSLSocketIO *>(newsocket->io); + + if (gnutls_init(&io->sess, GNUTLS_SERVER) != GNUTLS_E_SUCCESS) + throw SocketException("Unable to initialize SSL socket"); + + me->cred.SetupSession(io->sess); + gnutls_transport_set_int(io->sess, newsock); + + newsocket->flags[SF_ACCEPTING] = true; + this->FinishAccept(newsocket); + + return newsocket; +} + +SocketFlag SSLSocketIO::FinishAccept(ClientSocket *cs) +{ + if (cs->io == &NormalSocketIO) + throw SocketException("Attempting to finish connect uninitialized socket with SSL"); + else if (cs->flags[SF_ACCEPTED]) + return SF_ACCEPTED; + else if (!cs->flags[SF_ACCEPTING]) + throw SocketException("SSLSocketIO::FinishAccept called for a socket not accepted nor accepting?"); + + SSLSocketIO *io = anope_dynamic_static_cast<SSLSocketIO *>(cs->io); + + int ret = gnutls_handshake(io->sess); + if (ret < 0) + { + if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) + { + // gnutls_handshake() wants to read or write again; + // if gnutls_record_get_direction() returns 0 it wants to read, otherwise it wants to write. + if (gnutls_record_get_direction(io->sess) == 0) + { + SocketEngine::Change(cs, false, SF_WRITABLE); + SocketEngine::Change(cs, true, SF_READABLE); + } + else + { + SocketEngine::Change(cs, true, SF_WRITABLE); + SocketEngine::Change(cs, false, SF_READABLE); + } + return SF_ACCEPTING; + } + else + { + cs->OnError(Anope::string(gnutls_strerror(ret))); + cs->flags[SF_DEAD] = true; + cs->flags[SF_ACCEPTING] = false; + return SF_DEAD; + } + } + else + { + cs->flags[SF_ACCEPTED] = true; + cs->flags[SF_ACCEPTING] = false; + SocketEngine::Change(cs, false, SF_WRITABLE); + SocketEngine::Change(cs, true, SF_READABLE); + cs->OnAccept(); + return SF_ACCEPTED; + } +} + +void SSLSocketIO::Connect(ConnectionSocket *s, const Anope::string &target, int port) +{ + if (s->io == &NormalSocketIO) + throw SocketException("Attempting to connect uninitialized socket with SSL"); + + s->flags[SF_CONNECTING] = s->flags[SF_CONNECTED] = false; + + s->conaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, target, port); + int c = connect(s->GetFD(), &s->conaddr.sa, s->conaddr.size()); + if (c == -1) + { + if (Anope::LastErrorCode() != EINPROGRESS) + { + s->OnError(Anope::LastError()); + s->flags[SF_DEAD] = true; + return; + } + else + { + SocketEngine::Change(s, true, SF_WRITABLE); + s->flags[SF_CONNECTING] = true; + return; + } + } + else + { + s->flags[SF_CONNECTING] = true; + this->FinishConnect(s); + } +} + +SocketFlag SSLSocketIO::FinishConnect(ConnectionSocket *s) +{ + if (s->io == &NormalSocketIO) + throw SocketException("Attempting to finish connect uninitialized socket with SSL"); + else if (s->flags[SF_CONNECTED]) + return SF_CONNECTED; + else if (!s->flags[SF_CONNECTING]) + throw SocketException("SSLSocketIO::FinishConnect called for a socket not connected nor connecting?"); + + SSLSocketIO *io = anope_dynamic_static_cast<SSLSocketIO *>(s->io); + + if (io->sess == NULL) + { + if (gnutls_init(&io->sess, GNUTLS_CLIENT) != GNUTLS_E_SUCCESS) + throw SocketException("Unable to initialize SSL socket"); + me->cred.SetupSession(io->sess); + gnutls_transport_set_int(io->sess, s->GetFD()); + } + + int ret = gnutls_handshake(io->sess); + if (ret < 0) + { + if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) + { + // gnutls_handshake() wants to read or write again; + // if gnutls_record_get_direction() returns 0 it wants to read, otherwise it wants to write. + if (gnutls_record_get_direction(io->sess) == 0) + { + SocketEngine::Change(s, false, SF_WRITABLE); + SocketEngine::Change(s, true, SF_READABLE); + } + else + { + SocketEngine::Change(s, true, SF_WRITABLE); + SocketEngine::Change(s, false, SF_READABLE); + } + + return SF_CONNECTING; + } + else + { + s->OnError(Anope::string(gnutls_strerror(ret))); + s->flags[SF_CONNECTING] = false; + s->flags[SF_DEAD] = true; + return SF_DEAD; + } + } + else + { + s->flags[SF_CONNECTING] = false; + s->flags[SF_CONNECTED] = true; |
