diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/modules/extra/m_ssl_gnutls.cpp | 158 | ||||
-rw-r--r-- | src/modules/extra/ssl_cert.h | 93 |
2 files changed, 238 insertions, 13 deletions
diff --git a/src/modules/extra/m_ssl_gnutls.cpp b/src/modules/extra/m_ssl_gnutls.cpp index d39a1cc78..1ce19b29f 100644 --- a/src/modules/extra/m_ssl_gnutls.cpp +++ b/src/modules/extra/m_ssl_gnutls.cpp @@ -2,6 +2,7 @@ #include <vector> #include <gnutls/gnutls.h> +#include <gnutls/x509.h> #include "inspircd_config.h" #include "configreader.h" @@ -13,6 +14,8 @@ #include "hashcomp.h" #include "inspircd.h" +#include "ssl_cert.h" + /* $ModDesc: Provides SSL support for clients */ /* $CompileFlags: `libgnutls-config --cflags` */ /* $LinkerFlags: `libgnutls-config --libs` `perl ../gnutls_rpath.pl` */ @@ -214,6 +217,13 @@ class ModuleSSLGnuTLS : public Module ServerInstance->Log(DEBUG, "m_ssl_gnutls.so: Adding user %s to cull list", user->nick); culllist->AddItem(user, "SSL module unloading"); } + if (user->GetExt("ssl_cert", dummy) && isin(user->GetPort(), listenports)) + { + ssl_cert* tofree; + user->GetExt("ssl_cert", tofree); + delete tofree; + user->Shrink("ssl_cert"); + } } } @@ -265,7 +275,7 @@ class ModuleSSLGnuTLS : public Module */ gnutls_transport_set_ptr(session->sess, (gnutls_transport_ptr_t) fd); // Give gnutls the fd for the socket. - + Handshake(session); } @@ -273,13 +283,23 @@ class ModuleSSLGnuTLS : public Module { ServerInstance->Log(DEBUG, "OnRawSocketClose: %d", fd); CloseSession(&sessions[fd]); + + EventHandler* user = ServerInstance->SE->GetRef(fd); + + if ((user) && (user->GetExt("ssl_cert", dummy))) + { + ssl_cert* tofree; + user->GetExt("ssl_cert", tofree); + delete tofree; + user->Shrink("ssl_cert"); + } } virtual int OnRawSocketRead(int fd, char* buffer, unsigned int count, int &readresult) { issl_session* session = &sessions[fd]; - if(!session->sess) + if (!session->sess) { ServerInstance->Log(DEBUG, "m_ssl_gnutls.so: OnRawSocketRead: No session to read from"); readresult = 0; @@ -289,7 +309,7 @@ class ModuleSSLGnuTLS : public Module ServerInstance->Log(DEBUG, "m_ssl_gnutls.so: OnRawSocketRead(%d, buffer, %u, %d)", fd, count, readresult); - if(session->status == ISSL_HANDSHAKING_READ) + if (session->status == ISSL_HANDSHAKING_READ) { // The handshake isn't finished, try to finish it. @@ -305,7 +325,7 @@ class ModuleSSLGnuTLS : public Module return -1; } } - else if(session->status == ISSL_HANDSHAKING_WRITE) + else if (session->status == ISSL_HANDSHAKING_WRITE) { ServerInstance->Log(DEBUG, "m_ssl_gnutls.so: OnRawSocketRead: handshake wants to write data but we are currently reading"); return -1; @@ -313,7 +333,7 @@ class ModuleSSLGnuTLS : public Module // If we resumed the handshake then session->status will be ISSL_HANDSHAKEN. - if(session->status == ISSL_HANDSHAKEN) + if (session->status == ISSL_HANDSHAKEN) { // Is this right? Not sure if the unencrypted data is garaunteed to be the same length. // Read into the inbuffer, offset from the beginning by the amount of data we have that insp hasn't taken yet. @@ -321,7 +341,7 @@ class ModuleSSLGnuTLS : public Module int ret = gnutls_record_recv(session->sess, session->inbuf + session->inbufoffset, inbufsize - session->inbufoffset); - if(ret == 0) + if (ret == 0) { // Client closed connection. ServerInstance->Log(DEBUG, "m_ssl_gnutls.so: Client closed the connection"); @@ -329,9 +349,9 @@ class ModuleSSLGnuTLS : public Module CloseSession(session); return 1; } - else if(ret < 0) + else if (ret < 0) { - if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) + if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) { ServerInstance->Log(DEBUG, "m_ssl_gnutls.so: OnRawSocketRead: Not all SSL data read: %s", gnutls_strerror(ret)); return -1; @@ -350,8 +370,6 @@ class ModuleSSLGnuTLS : public Module // 'buffer' is 'count' long unsigned int length = ret + session->inbufoffset; - - ServerInstance->Log(DEBUG, "m_ssl_gnutls.so: OnRawSocketRead: Read %d bytes, now have %d waiting to be passed up", ret, length); if(count <= length) { @@ -415,7 +433,6 @@ class ModuleSSLGnuTLS : public Module ServerInstance->Log(DEBUG, "m_ssl_gnutls.so: OnRawSocketWrite: handshake wants to read data but we are currently writing"); } - ServerInstance->Log(DEBUG, "m_ssl_gnutls.so: OnRawSocketWrite: Adding %d bytes to the outgoing buffer", count); session->outbuf.append(sendbuffer, count); sendbuffer = session->outbuf.c_str(); count = session->outbuf.size(); @@ -443,7 +460,6 @@ class ModuleSSLGnuTLS : public Module } else { - ServerInstance->Log(DEBUG, "m_ssl_gnutls.so: OnRawSocketWrite: Successfully wrote %d bytes", ret); session->outbuf = session->outbuf.substr(ret); } } @@ -498,7 +514,7 @@ class ModuleSSLGnuTLS : public Module { int ret = gnutls_handshake(session->sess); - if(ret < 0) + if(ret < 0) { if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) { @@ -566,6 +582,8 @@ class ModuleSSLGnuTLS : public Module event->Send(ServerInstance); // Trigger the event. We don't care what module picks it up. DELETE(event); DELETE(metadata); + + VerifyCertificate(&sessions[user->GetFd()],user); } } @@ -592,6 +610,120 @@ class ModuleSSLGnuTLS : public Module session->sess = NULL; session->status = ISSL_NONE; } + + void VerifyCertificate(issl_session* session, userrec* user) + { + unsigned int status; + const gnutls_datum_t* cert_list; + int ret; + unsigned int cert_list_size, name_size; + gnutls_x509_crt_t cert; + char name[MAXBUF]; + ssl_cert* certinfo = new ssl_cert; + + user->Extend("ssl_cert",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); + + if (ret < 0) + { + certinfo->data.insert(std::make_pair("error",std::string(gnutls_strerror(ret)))); + return; + } + + if (status & GNUTLS_CERT_INVALID) + { + certinfo->data.insert(std::make_pair("invalid",ConvToStr(1))); + } + else + { + certinfo->data.insert(std::make_pair("invalid",ConvToStr(0))); + } + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + { + certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(1))); + } + else + { + certinfo->data.insert(std::make_pair("unknownsigner",ConvToStr(0))); + } + if (status & GNUTLS_CERT_REVOKED) + { + certinfo->data.insert(std::make_pair("revoked",ConvToStr(1))); + } + else + { + certinfo->data.insert(std::make_pair("revoked",ConvToStr(0))); + } + if (status & GNUTLS_CERT_SIGNER_NOT_CA) + { + certinfo->data.insert(std::make_pair("trusted",ConvToStr(0))); + } + else + { + certinfo->data.insert(std::make_pair("trusted",ConvToStr(1))); + } + + /* 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) + { + certinfo->data.insert(std::make_pair("error","No X509 keys sent")); + return; + } + + ret = gnutls_x509_crt_init(&cert); + if (ret < 0) + { + certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret))); + return; + } + + cert_list = gnutls_certificate_get_peers(session->sess, &cert_list_size); + if (cert_list == NULL) + { + certinfo->data.insert(std::make_pair("error","No certificate was found")); + return; + } + + /* This is not a real world example, since we only check the first + * certificate in the given chain. + */ + + ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); + if (ret < 0) + { + certinfo->data.insert(std::make_pair("error",gnutls_strerror(ret))); + return; + } + + name_size = sizeof(name); + gnutls_x509_crt_get_dn(cert, name, &name_size); + + certinfo->data.insert(std::make_pair("dn",name)); + + name_size = sizeof(name); + gnutls_x509_crt_get_issuer_dn(cert, name, &name_size); + + certinfo->data.insert(std::make_pair("issuer",name)); + + /* Beware here we do not check for errors. + */ + if ((gnutls_x509_crt_get_expiration_time(cert) < time(0)) || (gnutls_x509_crt_get_activation_time(cert) > time(0))) + { + certinfo->data.insert(std::make_pair("error","Not activated, or expired certificate")); + } + + gnutls_x509_crt_deinit(cert); + + return; + } + }; class ModuleSSLGnuTLSFactory : public ModuleFactory diff --git a/src/modules/extra/ssl_cert.h b/src/modules/extra/ssl_cert.h new file mode 100644 index 000000000..fbbd821ac --- /dev/null +++ b/src/modules/extra/ssl_cert.h @@ -0,0 +1,93 @@ +#ifndef __SSL_CERT_H__ +#define __SSL_CERT_H__ + +#include <map> +#include <string> + +typedef std::map<std::string,std::string> ssl_data; +typedef ssl_data::iterator ssl_data_iter; + +class ssl_cert +{ + const std::string empty; + + public: + ssl_data data; + + ssl_cert() : empty("") + { + } + + const std::string& GetDN() + { + ssl_data_iter ssldi = data.find("dn"); + + if (ssldi != data.end()) + return ssldi->second; + else + return empty; + } + + const std::string& GetIssuer() + { + ssl_data_iter ssldi = data.find("issuer"); + + if (ssldi != data.end()) + return ssldi->second; + else + return empty; + } + + const std::string& GetError() + { + ssl_data_iter ssldi = data.find("error"); + + if (ssldi != data.end()) + return ssldi->second; + else + return empty; + } + + bool IsTrusted() + { + ssl_data_iter ssldi = data.find("trusted"); + + if (ssldi != data.end()) + return (ssldi->second == "1"); + else + return false; + } + + bool IsInvalid() + { + ssl_data_iter ssldi = data.find("invalid"); + + if (ssldi != data.end()) + return (ssldi->second == "1"); + else + return false; + } + + bool IsUnknownSigner() + { + ssl_data_iter ssldi = data.find("unknownsigner"); + + if (ssldi != data.end()) + return (ssldi->second == "1"); + else + return false; + } + + bool IsRevoked() + { + ssl_data_iter ssldi = data.find("revoked"); + + if (ssldi != data.end()) + return (ssldi->second == "1"); + else + return false; + } +}; + +#endif + |