diff options
Diffstat (limited to 'src/modules/extra')
-rw-r--r-- | src/modules/extra/m_ssl_gnutls.cpp | 273 | ||||
-rw-r--r-- | src/modules/extra/m_ssl_openssl.cpp | 285 |
2 files changed, 226 insertions, 332 deletions
diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp index 7c19925dd..2add962fd 100644 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@ -543,58 +543,41 @@ namespace GnuTLS }; } -/** Represents an SSL user's extra data - */ -class issl_session +class GnuTLSIOHook : public SSLIOHook { -public: - StreamSocket* socket; + private: gnutls_session_t sess; issl_status status; - reference<ssl_cert> cert; reference<GnuTLS::Profile> profile; - issl_session() : socket(NULL), sess(NULL) {} -}; - -class GnuTLSIOHook : public SSLIOHook -{ - private: void InitSession(StreamSocket* user, bool me_server) { - issl_session* session = &sessions[user->GetFd()]; - - gnutls_init(&session->sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT); - session->socket = user; + gnutls_init(&sess, me_server ? GNUTLS_SERVER : GNUTLS_CLIENT); - session->profile->SetupSession(session->sess); - gnutls_transport_set_ptr(session->sess, reinterpret_cast<gnutls_transport_ptr_t>(session)); - gnutls_transport_set_push_function(session->sess, gnutls_push_wrapper); - gnutls_transport_set_pull_function(session->sess, gnutls_pull_wrapper); + profile->SetupSession(sess); + gnutls_transport_set_ptr(sess, reinterpret_cast<gnutls_transport_ptr_t>(user)); + gnutls_transport_set_push_function(sess, gnutls_push_wrapper); + gnutls_transport_set_pull_function(sess, gnutls_pull_wrapper); if (me_server) - gnutls_certificate_server_set_request(session->sess, GNUTLS_CERT_REQUEST); // Request client certificate if any. - - Handshake(session, user); + gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST); // Request client certificate if any. } - void CloseSession(issl_session* session) + void CloseSession() { - if (session->sess) + if (this->sess) { - gnutls_bye(session->sess, GNUTLS_SHUT_WR); - gnutls_deinit(session->sess); + gnutls_bye(this->sess, GNUTLS_SHUT_WR); + gnutls_deinit(this->sess); } - session->socket = NULL; - session->sess = NULL; - session->cert = NULL; - session->profile = NULL; - session->status = ISSL_NONE; + sess = NULL; + certificate = NULL; + status = ISSL_NONE; } - bool Handshake(issl_session* session, StreamSocket* user) + bool Handshake(StreamSocket* user) { - int ret = gnutls_handshake(session->sess); + int ret = gnutls_handshake(this->sess); if (ret < 0) { @@ -602,24 +585,24 @@ class GnuTLSIOHook : public SSLIOHook { // Handshake needs resuming later, read() or write() would have blocked. - if(gnutls_record_get_direction(session->sess) == 0) + if (gnutls_record_get_direction(this->sess) == 0) { // gnutls_handshake() wants to read() again. - session->status = ISSL_HANDSHAKING_READ; + this->status = ISSL_HANDSHAKING_READ; ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); } else { // gnutls_handshake() wants to write() again. - session->status = ISSL_HANDSHAKING_WRITE; + this->status = ISSL_HANDSHAKING_WRITE; ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE); } } else { user->SetError("Handshake Failed - " + std::string(gnutls_strerror(ret))); - CloseSession(session); - session->status = ISSL_CLOSING; + CloseSession(); + this->status = ISSL_CLOSING; } return false; @@ -627,9 +610,9 @@ class GnuTLSIOHook : public SSLIOHook else { // Change the seesion state - session->status = ISSL_HANDSHAKEN; + this->status = ISSL_HANDSHAKEN; - VerifyCertificate(session,user); + VerifyCertificate(); // Finish writing, if any left ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE); @@ -638,12 +621,9 @@ class GnuTLSIOHook : public SSLIOHook } } - void VerifyCertificate(issl_session* session, StreamSocket* user) + void VerifyCertificate() { - if (!session->sess || !user) - return; - - unsigned int status; + unsigned int certstatus; const gnutls_datum_t* cert_list; int ret; unsigned int cert_list_size; @@ -653,12 +633,12 @@ class GnuTLSIOHook : public SSLIOHook size_t digest_size = sizeof(digest); size_t name_size = sizeof(str); ssl_cert* certinfo = new ssl_cert; - session->cert = certinfo; + this->certificate = certinfo; /* This verification function uses the trusted CAs in the credentials * structure. So you must have installed one or more CA certificates. */ - ret = gnutls_certificate_verify_peers2(session->sess, &status); + ret = gnutls_certificate_verify_peers2(this->sess, &certstatus); if (ret < 0) { @@ -666,16 +646,16 @@ class GnuTLSIOHook : public SSLIOHook return; } - certinfo->invalid = (status & GNUTLS_CERT_INVALID); - certinfo->unknownsigner = (status & GNUTLS_CERT_SIGNER_NOT_FOUND); - certinfo->revoked = (status & GNUTLS_CERT_REVOKED); - certinfo->trusted = !(status & GNUTLS_CERT_SIGNER_NOT_CA); + certinfo->invalid = (certstatus & GNUTLS_CERT_INVALID); + certinfo->unknownsigner = (certstatus & GNUTLS_CERT_SIGNER_NOT_FOUND); + certinfo->revoked = (certstatus & GNUTLS_CERT_REVOKED); + certinfo->trusted = !(certstatus & GNUTLS_CERT_SIGNER_NOT_CA); /* Up to here the process is the same for X.509 certificates and * OpenPGP keys. From now on X.509 certificates are assumed. This can * be easily extended to work with openpgp keys as well. */ - if (gnutls_certificate_type_get(session->sess) != GNUTLS_CRT_X509) + if (gnutls_certificate_type_get(this->sess) != GNUTLS_CRT_X509) { certinfo->error = "No X509 keys sent"; return; @@ -689,7 +669,7 @@ class GnuTLSIOHook : public SSLIOHook } cert_list_size = 0; - cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size); + cert_list = gnutls_certificate_get_peers(this->sess, &cert_list_size); if (cert_list == NULL) { certinfo->error = "No certificate was found"; @@ -713,7 +693,7 @@ class GnuTLSIOHook : public SSLIOHook gnutls_x509_crt_get_issuer_dn(cert, str, &name_size); certinfo->issuer = str; - if ((ret = gnutls_x509_crt_get_fingerprint(cert, session->profile->GetHash(), digest, &digest_size)) < 0) + if ((ret = gnutls_x509_crt_get_fingerprint(cert, profile->GetHash(), digest, &digest_size)) < 0) { certinfo->error = gnutls_strerror(ret); } @@ -740,8 +720,12 @@ info_done_dealloc: static ssize_t gnutls_pull_wrapper(gnutls_transport_ptr_t session_wrap, void* buffer, size_t size) { - issl_session* session = reinterpret_cast<issl_session*>(session_wrap); - if (session->socket->GetEventMask() & FD_READ_WILL_BLOCK) + StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap); +#ifdef _WIN32 + GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook()); +#endif + + if (sock->GetEventMask() & FD_READ_WILL_BLOCK) { #ifdef _WIN32 gnutls_transport_set_errno(session->sess, EAGAIN); @@ -751,7 +735,7 @@ info_done_dealloc: return -1; } - int rv = ServerInstance->SE->Recv(session->socket, reinterpret_cast<char *>(buffer), size, 0); + int rv = ServerInstance->SE->Recv(sock, reinterpret_cast<char *>(buffer), size, 0); #ifdef _WIN32 if (rv < 0) @@ -766,14 +750,18 @@ info_done_dealloc: #endif if (rv < (int)size) - ServerInstance->SE->ChangeEventMask(session->socket, FD_READ_WILL_BLOCK); + ServerInstance->SE->ChangeEventMask(sock, FD_READ_WILL_BLOCK); return rv; } static ssize_t gnutls_push_wrapper(gnutls_transport_ptr_t session_wrap, const void* buffer, size_t size) { - issl_session* session = reinterpret_cast<issl_session*>(session_wrap); - if (session->socket->GetEventMask() & FD_WRITE_WILL_BLOCK) + StreamSocket* sock = reinterpret_cast<StreamSocket*>(session_wrap); +#ifdef _WIN32 + GnuTLSIOHook* session = static_cast<GnuTLSIOHook*>(sock->GetIOHook()); +#endif + + if (sock->GetEventMask() & FD_WRITE_WILL_BLOCK) { #ifdef _WIN32 gnutls_transport_set_errno(session->sess, EAGAIN); @@ -783,7 +771,7 @@ info_done_dealloc: return -1; } - int rv = ServerInstance->SE->Send(session->socket, reinterpret_cast<const char *>(buffer), size, 0); + int rv = ServerInstance->SE->Send(sock, reinterpret_cast<const char *>(buffer), size, 0); #ifdef _WIN32 if (rv < 0) @@ -798,75 +786,55 @@ info_done_dealloc: #endif if (rv < (int)size) - ServerInstance->SE->ChangeEventMask(session->socket, FD_WRITE_WILL_BLOCK); + ServerInstance->SE->ChangeEventMask(sock, FD_WRITE_WILL_BLOCK); return rv; } public: - issl_session* sessions; - - GnuTLSIOHook(Module* parent) - : SSLIOHook(parent, "ssl/gnutls") - { - sessions = new issl_session[ServerInstance->SE->GetMaxFds()]; - } - - ~GnuTLSIOHook() - { - delete[] sessions; - } - - void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE + GnuTLSIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool outbound, const reference<GnuTLS::Profile>& sslprofile) + : SSLIOHook(hookprov) + , sess(NULL) + , status(ISSL_NONE) + , profile(sslprofile) { - issl_session* session = &sessions[user->GetFd()]; - - /* For STARTTLS: Don't try and init a session on a socket that already has a session */ - if (session->sess) - return; - - InitSession(user, true); - } - - void OnStreamSocketConnect(StreamSocket* user) CXX11_OVERRIDE - { - InitSession(user, false); + InitSession(sock, outbound); + sock->AddIOHook(this); + Handshake(sock); } void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE { - CloseSession(&sessions[user->GetFd()]); + CloseSession(); } int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE { - issl_session* session = &sessions[user->GetFd()]; - - if (!session->sess) + if (!this->sess) { - CloseSession(session); + CloseSession(); user->SetError("No SSL session"); return -1; } - if (session->status == ISSL_HANDSHAKING_READ || session->status == ISSL_HANDSHAKING_WRITE) + if (this->status == ISSL_HANDSHAKING_READ || this->status == ISSL_HANDSHAKING_WRITE) { // The handshake isn't finished, try to finish it. - if(!Handshake(session, user)) + if (!Handshake(user)) { - if (session->status != ISSL_CLOSING) + if (this->status != ISSL_CLOSING) return 0; return -1; } } - // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN. + // If we resumed the handshake then this->status will be ISSL_HANDSHAKEN. - if (session->status == ISSL_HANDSHAKEN) + if (this->status == ISSL_HANDSHAKEN) { char* buffer = ServerInstance->GetReadBuffer(); size_t bufsiz = ServerInstance->Config->NetBufferSize; - int ret = gnutls_record_recv(session->sess, buffer, bufsiz); + int ret = gnutls_record_recv(this->sess, buffer, bufsiz); if (ret > 0) { recvq.append(buffer, ret); @@ -879,17 +847,17 @@ info_done_dealloc: else if (ret == 0) { user->SetError("Connection closed"); - CloseSession(session); + CloseSession(); return -1; } else { user->SetError(gnutls_strerror(ret)); - CloseSession(session); + CloseSession(); return -1; } } - else if (session->status == ISSL_CLOSING) + else if (this->status == ISSL_CLOSING) return -1; return 0; @@ -897,29 +865,27 @@ info_done_dealloc: int OnStreamSocketWrite(StreamSocket* user, std::string& sendq) CXX11_OVERRIDE { - issl_session* session = &sessions[user->GetFd()]; - - if (!session->sess) + if (!this->sess) { - CloseSession(session); + CloseSession(); user->SetError("No SSL session"); return -1; } - if (session->status == ISSL_HANDSHAKING_WRITE || session->status == ISSL_HANDSHAKING_READ) + if (this->status == ISSL_HANDSHAKING_WRITE || this->status == ISSL_HANDSHAKING_READ) { // The handshake isn't finished, try to finish it. - Handshake(session, user); - if (session->status != ISSL_CLOSING) + Handshake(user); + if (this->status != ISSL_CLOSING) return 0; return -1; } int ret = 0; - if (session->status == ISSL_HANDSHAKEN) + if (this->status == ISSL_HANDSHAKEN) { - ret = gnutls_record_send(session->sess, sendq.data(), sendq.length()); + ret = gnutls_record_send(this->sess, sendq.data(), sendq.length()); if (ret == (int)sendq.length()) { @@ -940,7 +906,7 @@ info_done_dealloc: else // (ret < 0) { user->SetError(gnutls_strerror(ret)); - CloseSession(session); + CloseSession(); return -1; } } @@ -948,16 +914,8 @@ info_done_dealloc: return 0; } - ssl_cert* GetCertificate(StreamSocket* sock) CXX11_OVERRIDE - { - int fd = sock->GetFd(); - issl_session* session = &sessions[fd]; - return session->cert; - } - void TellCiphersAndFingerprint(LocalUser* user) { - const gnutls_session_t& sess = sessions[user->eh.GetFd()].sess; if (sess) { std::string text = "*** You are connected using SSL cipher '"; @@ -966,13 +924,14 @@ info_done_dealloc: text.append("-").append(UnknownIfNULL(gnutls_cipher_get_name(gnutls_cipher_get(sess)))).append("-"); text.append(UnknownIfNULL(gnutls_mac_get_name(gnutls_mac_get(sess)))).append("'"); - ssl_cert* cert = sessions[user->eh.GetFd()].cert; - if (!cert->fingerprint.empty()) - text += " and your SSL fingerprint is " + cert->fingerprint; + if (!certificate->fingerprint.empty()) + text += " and your SSL fingerprint is " + certificate->fingerprint; user->WriteNotice(text); } } + + GnuTLS::Profile* GetProfile() { return profile; } }; int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_datum_t* req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t* sign_algos, int sign_algos_length, cert_cb_last_param_type* st) @@ -983,8 +942,8 @@ int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_d st->cert_type = GNUTLS_CRT_X509; st->key_type = GNUTLS_PRIVKEY_X509; #endif - issl_session* session = reinterpret_cast<issl_session*>(gnutls_transport_get_ptr(sess)); - GnuTLS::X509Credentials& cred = session->profile->GetX509Credentials(); + StreamSocket* sock = reinterpret_cast<StreamSocket*>(gnutls_transport_get_ptr(sess)); + GnuTLS::X509Credentials& cred = static_cast<GnuTLSIOHook*>(sock->GetIOHook())->GetProfile()->GetX509Credentials(); st->ncerts = cred.certs.size(); st->cert.x509 = cred.certs.raw(); @@ -994,15 +953,41 @@ int GnuTLS::X509Credentials::cert_callback(gnutls_session_t sess, const gnutls_d return 0; } +class GnuTLSIOHookProvider : public refcountbase, public IOHookProvider +{ + reference<GnuTLS::Profile> profile; + + public: + GnuTLSIOHookProvider(Module* mod, reference<GnuTLS::Profile>& prof) + : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL) + , profile(prof) + { + ServerInstance->Modules->AddService(*this); + } + + ~GnuTLSIOHookProvider() + { + ServerInstance->Modules->DelService(*this); + } + + void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE + { + new GnuTLSIOHook(this, sock, true, profile); + } + + void OnConnect(StreamSocket* sock) CXX11_OVERRIDE + { + new GnuTLSIOHook(this, sock, false, profile); + } +}; + class ModuleSSLGnuTLS : public Module { - typedef std::vector<reference<GnuTLS::Profile> > ProfileList; + typedef std::vector<reference<GnuTLSIOHookProvider> > ProfileList; // First member of the class, gets constructed first and destructed last GnuTLS::Init libinit; - GnuTLSIOHook iohook; - std::string sslports; RandGen randhandler; @@ -1026,7 +1011,7 @@ class ModuleSSLGnuTLS : public Module try { reference<GnuTLS::Profile> profile(GnuTLS::Profile::Create(defname, tag)); - newprofiles.push_back(profile); + newprofiles.push_back(new GnuTLSIOHookProvider(this, profile)); } catch (CoreException& ex) { @@ -1057,7 +1042,7 @@ class ModuleSSLGnuTLS : public Module throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason()); } - newprofiles.push_back(profile); + newprofiles.push_back(new GnuTLSIOHookProvider(this, profile)); } // New profiles are ok, begin using them @@ -1066,7 +1051,7 @@ class ModuleSSLGnuTLS : public Module } public: - ModuleSSLGnuTLS() : iohook(this) + ModuleSSLGnuTLS() { #ifndef GNUTLS_HAS_RND gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); @@ -1144,7 +1129,7 @@ class ModuleSSLGnuTLS : public Module { LocalUser* user = IS_LOCAL(static_cast<User*>(item)); - if (user && user->eh.GetIOHook() == &iohook) + if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == 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. @@ -1164,27 +1149,11 @@ class ModuleSSLGnuTLS : public Module tokens["SSL"] = sslports; } - void OnHookIO(StreamSocket* user, ListenSocket* lsb) CXX11_OVERRIDE - { - if (!user->GetIOHook()) - { - std::string profilename = lsb->bind_tag->getString("ssl"); - for (ProfileList::const_iterator i = profiles.begin(); i != profiles.end(); ++i) - { - if ((*i)->GetName() == profilename) - { - iohook.sessions[user->GetFd()].profile = *i; - user->AddIOHook(&iohook); - break; - } - } - } - } - void OnUserConnect(LocalUser* user) CXX11_OVERRIDE { - if (user->eh.GetIOHook() == &iohook) - iohook.TellCiphersAndFingerprint(user); + IOHook* hook = user->eh.GetIOHook(); + if (hook && hook->prov->creator == this) + static_cast<GnuTLSIOHook*>(hook)->TellCiphersAndFingerprint(user); } }; diff --git a/src/modules/extra/m_ssl_openssl.cpp b/src/modules/extra/m_ssl_openssl.cpp index 11f4a365e..962350e1c 100644 --- a/src/modules/extra/m_ssl_openssl.cpp +++ b/src/modules/extra/m_ssl_openssl.cpp @@ -235,26 +235,6 @@ namespace OpenSSL }; } -/** Represents an SSL user's extra data - */ -class issl_session -{ -public: - SSL* sess; - issl_status status; - reference<ssl_cert> cert; - reference<OpenSSL::Profile> profile; - - bool outbound; - bool data_to_write; - - issl_session() - { - outbound = false; - data_to_write = false; - } -}; - static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx) { /* XXX: This will allow self signed certificates. @@ -272,34 +252,40 @@ static int OnVerify(int preverify_ok, X509_STORE_CTX *ctx) class OpenSSLIOHook : public SSLIOHook { private: - bool Handshake(StreamSocket* user, issl_session* session) + SSL* sess; + issl_status status; + const bool outbound; + bool data_to_write; + reference<OpenSSL::Profile> profile; + + bool Handshake(StreamSocket* user) { int ret; - if (session->outbound) - ret = SSL_connect(session->sess); + if (outbound) + ret = SSL_connect(sess); else - ret = SSL_accept(session->sess); + ret = SSL_accept(sess); if (ret < 0) { - int err = SSL_get_error(session->sess, ret); + int err = SSL_get_error(sess, ret); if (err == SSL_ERROR_WANT_READ) { ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); - session->status = ISSL_HANDSHAKING; + this->status = ISSL_HANDSHAKING; return true; } else if (err == SSL_ERROR_WANT_WRITE) { ServerInstance->SE->ChangeEventMask(user, FD_WANT_NO_READ | FD_WANT_SINGLE_WRITE); - session->status = ISSL_HANDSHAKING; + this->status = ISSL_HANDSHAKING; return true; } else { - CloseSession(session); + CloseSession(); } return false; @@ -307,9 +293,9 @@ class OpenSSLIOHook : public SSLIOHook else if (ret > 0) { // Handshake complete. - VerifyCertificate(session, user); + VerifyCertificate(); - session->status = ISSL_OPEN; + status = ISSL_OPEN; ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE | FD_ADD_TRIAL_WRITE); @@ -317,38 +303,35 @@ class OpenSSLIOHook : public SSLIOHook } else if (ret == 0) { - CloseSession(session); + CloseSession(); return true; } return true; } - void CloseSession(issl_session* session) + void CloseSession() { - if (session->sess) + if (sess) { - SSL_shutdown(session->sess); - SSL_free(session->sess); + SSL_shutdown(sess); + SSL_free(sess); } - - session->sess = NULL; - session->status = ISSL_NONE; + sess = NULL; + certificate = NULL; + status = ISSL_NONE; errno = EIO; } - void VerifyCertificate(issl_session* session, StreamSocket* user) + void VerifyCertificate() { - if (!session->sess || !user) - return; - X509* cert; ssl_cert* certinfo = new ssl_cert; - session->cert = certinfo; + this->certificate = certinfo; unsigned int n; unsigned char md[EVP_MAX_MD_SIZE]; - cert = SSL_get_peer_certificate((SSL*)session->sess); + cert = SSL_get_peer_certificate(sess); if (!cert) { @@ -356,7 +339,7 @@ class OpenSSLIOHook : public SSLIOHook return; } - certinfo->invalid = (SSL_get_verify_result(session->sess) != X509_V_OK); + certinfo->invalid = (SSL_get_verify_result(sess) != X509_V_OK); if (!SelfSigned) { @@ -372,7 +355,7 @@ class OpenSSLIOHook : public SSLIOHook certinfo->dn = X509_NAME_oneline(X509_get_subject_name(cert),0,0); certinfo->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),0,0); - if (!X509_digest(cert, session->profile->GetDigest(), md, &n)) + if (!X509_digest(cert, profile->GetDigest(), md, &n)) { certinfo->error = "Out of memory generating fingerprint"; } @@ -390,129 +373,73 @@ class OpenSSLIOHook : public SSLIOHook } public: - issl_session* sessions; - - OpenSSLIOHook(Module* mod) - : SSLIOHook(mod, "ssl/openssl") + OpenSSLIOHook(IOHookProvider* hookprov, StreamSocket* sock, bool is_outbound, SSL* session, const reference<OpenSSL::Profile>& sslprofile) + : SSLIOHook(hookprov) + , sess(session) + , status(ISSL_NONE) + , outbound(is_outbound) + , data_to_write(false) + , profile(sslprofile) { - sessions = new issl_session[ServerInstance->SE->GetMaxFds()]; - } - - ~OpenSSLIOHook() - { - delete[] sessions; - } - - void OnStreamSocketAccept(StreamSocket* user, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE - { - int fd = user->GetFd(); - - issl_session* session = &sessions[fd]; - - session->sess = session->profile->CreateServerSession(); - session->status = ISSL_NONE; - session->outbound = false; - session->cert = NULL; - - if (session->sess == NULL) + if (sess == NULL) return; + if (SSL_set_fd(sess, sock->GetFd()) == 0) + throw ModuleException("Can't set fd with SSL_set_fd: " + ConvToStr(sock->GetFd())); - if (SSL_set_fd(session->sess, fd) == 0) - { - ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Can't set fd with SSL_set_fd: %d", fd); - return; - } - - Handshake(user, session); - } - - void OnStreamSocketConnect(StreamSocket* user) CXX11_OVERRIDE - { - int fd = user->GetFd(); - /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */ - if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() -1)) - return; - - issl_session* session = &sessions[fd]; - - session->sess = session->profile->CreateClientSession(); - session->status = ISSL_NONE; - session->outbound = true; - - if (session->sess == NULL) - return; - - if (SSL_set_fd(session->sess, fd) == 0) - { - ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "BUG: Can't set fd with SSL_set_fd: %d", fd); - return; - } - - Handshake(user, session); + sock->AddIOHook(this); + Handshake(sock); } void OnStreamSocketClose(StreamSocket* user) CXX11_OVERRIDE { - int fd = user->GetFd(); - /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */ - if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1)) - return; - - CloseSession(&sessions[fd]); + CloseSession(); } int OnStreamSocketRead(StreamSocket* user, std::string& recvq) CXX11_OVERRIDE { - int fd = user->GetFd(); - /* Are there any possibilities of an out of range fd? Hope not, but lets be paranoid */ - if ((fd < 0) || (fd > ServerInstance->SE->GetMaxFds() - 1)) - return -1; - - issl_session* session = &sessions[fd]; - - if (!session->sess) + if (!sess) { - CloseSession(session); + CloseSession(); return -1; } - if (session->status == ISSL_HANDSHAKING) + if (status == ISSL_HANDSHAKING) { // The handshake isn't finished and it wants to read, try to finish it. - if (!Handshake(user, session)) + if (!Handshake(user)) { // Couldn't resume handshake. - if (session->status == ISSL_NONE) + if (status == ISSL_NONE) return -1; return 0; } } - // If we resumed the handshake then session->status will be ISSL_OPEN + // If we resumed the handshake then this->status will be ISSL_OPEN - if (session->status == ISSL_OPEN) + if (status == ISSL_OPEN) { char* buffer = ServerInstance->GetReadBuffer(); size_t bufsiz = ServerInstance->Config->NetBufferSize; - int ret = SSL_read(session->sess, buffer, bufsiz); + int ret = SSL_read(sess, buffer, bufsiz); if (ret > 0) { recvq.append(buffer, ret); - if (session->data_to_write) + if (data_to_write) ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_SINGLE_WRITE); return 1; } else if (ret == 0) { // Client closed connection. - CloseSession(session); + CloseSession(); user->SetError("Connection closed"); return -1; } else if (ret < 0) { - int err = SSL_get_error(session->sess, ret); + int err = SSL_get_error(sess, ret); if (err == SSL_ERROR_WANT_READ) { @@ -526,7 +453,7 @@ class OpenSSLIOHook : public SSLIOHook } else { - CloseSession(session); + CloseSession(); return -1; } } @@ -537,35 +464,31 @@ class OpenSSLIOHook : public SSLIOHook int OnStreamSocketWrite(StreamSocket* user, std::string& buffer) CXX11_OVERRIDE { - int fd = user->GetFd(); - - issl_session* session = &sessions[fd]; - - if (!session->sess) + if (!sess) { - CloseSession(session); + CloseSession(); return -1; } - session->data_to_write = true; + data_to_write = true; - if (session->status == ISSL_HANDSHAKING) + if (status == ISSL_HANDSHAKING) { - if (!Handshake(user, session)) + if (!Handshake(user)) { // Couldn't resume handshake. - if (session->status == ISSL_NONE) + if (status == ISSL_NONE) return -1; return 0; } } - if (session->status == ISSL_OPEN) + if (status == ISSL_OPEN) { - int ret = SSL_write(session->sess, buffer.data(), buffer.size()); + int ret = SSL_write(sess, buffer.data(), buffer.size()); if (ret == (int)buffer.length()) { - session->data_to_write = false; + data_to_write = false; ServerInstance->SE->ChangeEventMask(user, FD_WANT_POLL_READ | FD_WANT_NO_WRITE); return 1; } @@ -577,12 +500,12 @@ class OpenSSLIOHook : public SSLIOHook } else if (ret == 0) { - CloseSession(session); + CloseSession(); return -1; } else if (ret < 0) { - int err = SSL_get_error(session->sess, ret); + int err = SSL_get_error(sess, ret); if (err == SSL_ERROR_WANT_WRITE) { @@ -596,7 +519,7 @@ class OpenSSLIOHook : public SSLIOHook } else { - CloseSession(session); + CloseSession(); return -1; } } @@ -604,20 +527,12 @@ class OpenSSLIOHook : public SSLIOHook return 0; } - ssl_cert* GetCertificate(StreamSocket* sock) CXX11_OVERRIDE - { - int fd = sock->GetFd(); - issl_session* session = &sessions[fd]; - return session->cert; - } - void TellCiphersAndFingerprint(LocalUser* user) { - issl_session& s = sessions[user->eh.GetFd()]; - if (s.sess) + if (sess) { - std::string text = "*** You are connected using SSL cipher '" + std::string(SSL_get_cipher(s.sess)) + "'"; - const std::string& fingerprint = s.cert->fingerprint; + std::string text = "*** You are connected using SSL cipher '" + std::string(SSL_get_cipher(sess)) + "'"; + const std::string& fingerprint = certificate->fingerprint; if (!fingerprint.empty()) text += " and your SSL fingerprint is " + fingerprint; @@ -626,12 +541,39 @@ class OpenSSLIOHook : public SSLIOHook } }; +class OpenSSLIOHookProvider : public refcountbase, public IOHookProvider +{ + reference<OpenSSL::Profile> profile; + + public: + OpenSSLIOHookProvider(Module* mod, reference<OpenSSL::Profile>& prof) + : IOHookProvider(mod, "ssl/" + prof->GetName(), IOHookProvider::IOH_SSL) + , profile(prof) + { + ServerInstance->Modules->AddService(*this); + } + + ~OpenSSLIOHookProvider() + { + ServerInstance->Modules->DelService(*this); + } + + void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE + { + new OpenSSLIOHook(this, sock, false, profile->CreateServerSession(), profile); + } + + void OnConnect(StreamSocket* sock) CXX11_OVERRIDE + { + new OpenSSLIOHook(this, sock, true, profile->CreateClientSession(), profile); + } +}; + class ModuleSSLOpenSSL : public Module { - typedef std::vector<reference<OpenSSL::Profile> > ProfileList; + typedef std::vector<reference<OpenSSLIOHookProvider> > ProfileList; std::string sslports; - OpenSSLIOHook iohook; ProfileList profiles; void ReadProfiles() @@ -648,7 +590,7 @@ class ModuleSSLOpenSSL : public Module try { reference<OpenSSL::Profile> profile(new OpenSSL::Profile(defname, tag)); - newprofiles.push_back(profile); + newprofiles.push_back(new OpenSSLIOHookProvider(this, profile)); } catch (OpenSSL::Exception& ex) { @@ -679,14 +621,14 @@ class ModuleSSLOpenSSL : public Module throw ModuleException("Error while initializing SSL profile \"" + name + "\" at " + tag->getTagLocation() + " - " + ex.GetReason()); } - newprofiles.push_back(profile); + newprofiles.push_back(new OpenSSLIOHookProvider(this, profile)); } profiles.swap(newprofiles); } public: - ModuleSSLOpenSSL() : iohook(this) + ModuleSSLOpenSSL() { // Initialize OpenSSL SSL_library_init(); @@ -698,24 +640,6 @@ class ModuleSSLOpenSSL : public Module ReadProfiles(); } - void OnHookIO(StreamSocket* user, ListenSocket* lsb) CXX11_OVERRIDE - { - if (user->GetIOHook()) - return; - - ConfigTag* tag = lsb->bind_tag; - std::string profilename = tag->getString("ssl"); - for (ProfileList::const_iterator i = profiles.begin(); i != profiles.end(); ++i) - { - if ((*i)->GetName() == profilename) - { - iohook.sessions[user->GetFd()].profile = *i; - user->AddIOHook(&iohook); - break; - } - } - } - void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { sslports.clear(); @@ -778,8 +702,9 @@ class ModuleSSLOpenSSL : public Module void OnUserConnect(LocalUser* user) CXX11_OVERRIDE { - if (user->eh.GetIOHook() == &iohook) - iohook.TellCiphersAndFingerprint(user); + IOHook* hook = user->eh.GetIOHook(); + if (hook && hook->prov->creator == this) + static_cast<OpenSSLIOHook*>(hook)->TellCiphersAndFingerprint(user); } void OnCleanup(int target_type, void* item) CXX11_OVERRIDE @@ -788,7 +713,7 @@ class ModuleSSLOpenSSL : public Module { LocalUser* user = IS_LOCAL((User*)item); - if (user && user->eh.GetIOHook() == &iohook) + if (user && user->eh.GetIOHook() && user->eh.GetIOHook()->prov->creator == 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. |