-/* +------------------------------------+
- * | Inspire Internet Relay Chat Daemon |
- * +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
*
- * InspIRCd: (C) 2002-2009 InspIRCd Development Team
- * See: http://wiki.inspircd.org/Credits
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ * Copyright (C) 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
*
- * This program is free but copyrighted software; see
- * the file COPYING for details.
+ * 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 <http://www.gnu.org/licenses/>.
*/
+
#include "inspircd.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "ssl.h"
#ifdef WINDOWS
-#pragma comment(lib, "libeay32MTd")
-#pragma comment(lib, "ssleay32MTd")
-#undef MAX_DESCRIPTORS
-#define MAX_DESCRIPTORS 10000
+# pragma comment(lib, "libcrypto.lib")
+# pragma comment(lib, "libssl.lib")
+# pragma comment(lib, "user32.lib")
+# pragma comment(lib, "advapi32.lib")
+# pragma comment(lib, "libgcc.lib")
+# pragma comment(lib, "libmingwex.lib")
+# pragma comment(lib, "gdi32.lib")
+# undef MAX_DESCRIPTORS
+# define MAX_DESCRIPTORS 10000
#endif
/* $ModDesc: Provides SSL support for clients */
/* $CompileFlags: if(!"USE_FREEBSD_BASE_SSL") pkgconfversion("openssl","0.9.7") pkgconfincludes("openssl","/openssl/ssl.h","") */
/* $LinkerFlags: if(!"USE_FREEBSD_BASE_SSL") rpath("pkg-config --libs openssl") pkgconflibs("openssl","/libssl.so","-lssl -lcrypto -ldl") */
-/* $ModDep: transport.h */
/* $NoPedantic */
-/* $CopyInstall: conf/key.pem $(CONPATH) */
-/* $CopyInstall: conf/cert.pem $(CONPATH) */
enum issl_status { ISSL_NONE, ISSL_HANDSHAKING, ISSL_OPEN };
/** Represents an SSL user's extra data
*/
-class issl_session : public classbase
+class issl_session
{
public:
SSL* sess;
issl_status status;
+ reference<ssl_cert> cert;
int fd;
bool outbound;
char cipher[MAXBUF];
- std::string keyfile;
- std::string certfile;
- std::string cafile;
- // std::string crlfile;
- std::string dhfile;
std::string sslports;
+ bool use_sha;
+ ServiceProvider iohook;
public:
- ModuleSSLOpenSSL()
+ ModuleSSLOpenSSL() : iohook(this, "ssl/openssl", SERVICE_IOHOOK)
{
- ServerInstance->Modules->PublishInterface("BufferedSocketHook", this);
-
sessions = new issl_session[ServerInstance->SE->GetMaxFds()];
// Not rehashable...because I cba to reduce all the sizes of existing buffers.
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
SSL_CTX_set_verify(clictx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+ }
+ void init()
+ {
// Needs the flag as it ignores a plain /rehash
OnModuleRehash(NULL,"ssl");
- Implementation eventlist[] = {
- I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnPostConnect,
- I_OnHookIO };
+ Implementation eventlist[] = { I_On005Numeric, I_OnRehash, I_OnModuleRehash, I_OnHookIO, I_OnUserConnect };
ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
+ ServerInstance->Modules->AddService(iohook);
}
- void OnHookIO(StreamSocket* user, ListenSocketBase* lsb)
+ void OnHookIO(StreamSocket* user, ListenSocket* lsb)
{
- if (!user->GetIOHook() && lsb->hook == "openssl")
+ if (!user->GetIOHook() && lsb->bind_tag->getString("ssl") == "openssl")
{
/* Hook the user with our module */
user->AddIOHook(this);
void OnRehash(User* user)
{
- ConfigReader Conf;
-
sslports.clear();
- for (size_t i = 0; i < ServerInstance->ports.size(); i++)
+ ConfigTag* Conf = ServerInstance->Config->ConfValue("openssl");
+
+ if (Conf->getBool("showports", true))
{
- ListenSocketBase* port = ServerInstance->ports[i];
- if (port->hook != "openssl")
- continue;
-
- std::string portid = port->GetBindDesc();
- ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %s", portid.c_str());
- if (port->type == "clients" && port->GetIP() != "127.0.0.1")
- sslports.append(portid).append(";");
- }
+ sslports = Conf->getString("advertisedports");
+ if (!sslports.empty())
+ return;
- if (!sslports.empty())
- sslports.erase(sslports.end() - 1);
+ for (size_t i = 0; i < ServerInstance->ports.size(); i++)
+ {
+ ListenSocket* port = ServerInstance->ports[i];
+ if (port->bind_tag->getString("ssl") != "openssl")
+ continue;
+
+ const std::string& portid = port->bind_desc;
+ ServerInstance->Logs->Log("m_ssl_openssl", DEFAULT, "m_ssl_openssl.so: Enabling SSL for port %s", portid.c_str());
+
+ if (port->bind_tag->getString("type", "clients") == "clients" && port->bind_addr != "127.0.0.1")
+ {
+ /*
+ * Found an SSL port for clients that is not bound to 127.0.0.1 and handled by us, display
+ * the IP:port in ISUPPORT.
+ *
+ * We used to advertise all ports seperated by a ';' char that matched the above criteria,
+ * but this resulted in too long ISUPPORT lines if there were lots of ports to be displayed.
+ * To solve this by default we now only display the first IP:port found and let the user
+ * configure the exact value for the 005 token, if necessary.
+ */
+ sslports = portid;
+ break;
+ }
+ }
+ }
}
void OnModuleRehash(User* user, const std::string ¶m)
if (param != "ssl")
return;
+ std::string keyfile;
+ std::string certfile;
+ std::string cafile;
+ std::string dhfile;
OnRehash(user);
- ConfigReader Conf;
-
- cafile = Conf.ReadValue("openssl", "cafile", 0);
- certfile = Conf.ReadValue("openssl", "certfile", 0);
- keyfile = Conf.ReadValue("openssl", "keyfile", 0);
- dhfile = Conf.ReadValue("openssl", "dhfile", 0);
-
- // Set all the default values needed.
- if (cafile.empty())
- cafile = "conf/ca.pem";
+ ConfigTag* conf = ServerInstance->Config->ConfValue("openssl");
- if (certfile.empty())
- certfile = "conf/cert.pem";
+ cafile = conf->getString("cafile", "conf/ca.pem");
+ certfile = conf->getString("certfile", "conf/cert.pem");
+ keyfile = conf->getString("keyfile", "conf/key.pem");
+ dhfile = conf->getString("dhfile", "conf/dhparams.pem");
+ std::string hash = conf->getString("hash", "md5");
+ if (hash != "sha1" && hash != "md5")
+ throw ModuleException("Unknown hash type " + hash);
+ use_sha = (hash == "sha1");
- if (keyfile.empty())
- keyfile = "conf/key.pem";
+ std::string ciphers = conf->getString("ciphers", "");
- if (dhfile.empty())
- dhfile = "conf/dhparams.pem";
+ if (!ciphers.empty())
+ {
+ if ((!SSL_CTX_set_cipher_list(ctx, ciphers.c_str())) || (!SSL_CTX_set_cipher_list(clictx, ciphers.c_str())))
+ {
+ ServerInstance->Logs->Log("m_ssl_openssl",DEFAULT, "m_ssl_openssl.so: Can't set cipher list to %s.", ciphers.c_str());
+ ERR_print_errors_cb(error_callback, this);
+ }
+ }
/* Load our keys and certificates
* NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
{
SSL_CTX_free(ctx);
SSL_CTX_free(clictx);
- ServerInstance->Modules->UnpublishInterface("BufferedSocketHook", this);
delete[] sessions;
}
+ void OnUserConnect(LocalUser* user)
+ {
+ if (user->eh.GetIOHook() == this)
+ {
+ if (sessions[user->eh.GetFd()].sess)
+ {
+ if (!sessions[user->eh.GetFd()].cert->fingerprint.empty())
+ user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\""
+ " and your SSL fingerprint is %s", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess), sessions[user->eh.GetFd()].cert->fingerprint.c_str());
+ else
+ user->WriteServ("NOTICE %s :*** You are connected using SSL cipher \"%s\"", user->nick.c_str(), SSL_get_cipher(sessions[user->eh.GetFd()].sess));
+ }
+ }
+ }
+
void OnCleanup(int target_type, void* item)
{
if (target_type == TYPE_USER)
{
- User* user = (User*)item;
+ LocalUser* user = IS_LOCAL((User*)item);
- if (user->GetIOHook() == this)
+ if (user && user->eh.GetIOHook() == 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->DelIOHook();
}
}
}
Version GetVersion()
{
- return Version("Provides SSL support for clients", VF_VENDOR, API_VERSION);
+ return Version("Provides SSL support for clients", VF_VENDOR);
}
-
void OnRequest(Request& request)
{
- Module* sslinfo = ServerInstance->Modules->Find("m_sslinfo.so");
- if (sslinfo)
- sslinfo->OnRequest(request);
- }
+ if (strcmp("GET_SSL_CERT", request.id) == 0)
+ {
+ SocketCertificateRequest& req = static_cast<SocketCertificateRequest&>(request);
+ int fd = req.sock->GetFd();
+ issl_session* session = &sessions[fd];
+ req.cert = session->cert;
+ }
+ }
void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
{
session->sess = SSL_new(ctx);
session->status = ISSL_NONE;
session->outbound = false;
+ session->cert = NULL;
if (session->sess == NULL)
return;
char* buffer = ServerInstance->GetReadBuffer();
size_t bufsiz = ServerInstance->Config->NetBufferSize;
int ret = SSL_read(session->sess, buffer, bufsiz);
-
+
if (ret > 0)
{
recvq.append(buffer, ret);
+ if (session->data_to_write)
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE);
return 1;
}
else if (ret == 0)
}
else if (err == SSL_ERROR_WANT_READ)
{
- ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE);
+ ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ);
return 0;
}
else
return 0;
}
- bool Handshake(EventHandler* user, issl_session* session)
+ bool Handshake(StreamSocket* user, issl_session* session)
{
int ret;
errno = EIO;
}
- void VerifyCertificate(issl_session* session, Extensible* user)
+ void VerifyCertificate(issl_session* session, StreamSocket* user)
{
if (!session->sess || !user)
return;
- Module* sslinfo = ServerInstance->Modules->Find("m_sslinfo.so");
- if (!sslinfo)
- return;
-
X509* cert;
ssl_cert* certinfo = new ssl_cert;
+ session->cert = certinfo;
unsigned int n;
unsigned char md[EVP_MAX_MD_SIZE];
- const EVP_MD *digest = EVP_md5();
+ const EVP_MD *digest = use_sha ? EVP_sha1() : EVP_md5();
cert = SSL_get_peer_certificate((SSL*)session->sess);
if (!cert)
{
certinfo->error = "Could not get peer certificate: "+std::string(get_error());
- SSLCertSubmission(user, this, sslinfo, certinfo);
return;
}
}
X509_free(cert);
- SSLCertSubmission(user, this, sslinfo, certinfo);
}
-
- void Prioritize()
- {
- Module* server = ServerInstance->Modules->Find("m_spanningtree.so");
- ServerInstance->Modules->SetPriority(this, I_OnPostConnect, PRIORITY_AFTER, &server);
- }
-
};
static int error_callback(const char *str, size_t len, void *u)