From f9fb628aedecc86a081672e50a08de966ec66fc8 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Mon, 18 May 2015 16:48:41 +0200 Subject: [PATCH] Implemented certificate fingerprint: available through /WHOIS and synch'ed network-wide (via ModData system). Thanks to DBoyz and Nath (#4136) for the contributed patches. Initial commit (but it works). --- modules.conf | 1 + src/modules/Makefile.in | 6 +- src/modules/certfp.c | 146 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/modules/certfp.c diff --git a/modules.conf b/modules.conf index 75517a827..235aeda2e 100644 --- a/modules.conf +++ b/modules.conf @@ -107,6 +107,7 @@ loadmodule "modules/m_nopost"; loadmodule "modules/m_cap"; loadmodule "modules/m_sasl"; loadmodule "modules/m_md"; +loadmodule "modules/certfp"; loadmodule "modules/cap_invitenotify"; /*** Channel modes ***/ diff --git a/src/modules/Makefile.in b/src/modules/Makefile.in index d42a4aeaa..97a705989 100644 --- a/src/modules/Makefile.in +++ b/src/modules/Makefile.in @@ -55,7 +55,7 @@ R_MODULES= \ m_mode.so m_watch.so m_part.so m_join.so m_motd.so m_opermotd.so \ m_botmotd.so m_lusers.so m_names.so m_svsnolag.so m_addmotd.so \ m_svslusers.so m_starttls.so m_nopost.so m_cap.so \ - m_sasl.so cap_invitenotify.so m_md.so + m_sasl.so cap_invitenotify.so m_md.so certfp.so MODULES=cloak.so $(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ @@ -492,6 +492,10 @@ m_md.so: m_md.c $(INCLUDES) $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ -o m_md.so m_md.c +certfp.so: certfp.c $(INCLUDES) + $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ + -o certfp.so certfp.c + ############################################################################# # capabilities ############################################################################# diff --git a/src/modules/certfp.c b/src/modules/certfp.c new file mode 100644 index 000000000..440a971d4 --- /dev/null +++ b/src/modules/certfp.c @@ -0,0 +1,146 @@ +/* + * Certificate Fingerprint Module + * This grabs the SHA256 fingerprint of the SSL/TLS client certificate + * the user is using, shares it with the other servers (and rest of + * UnrealIRCd) and shows it in /WHOIS etc. + * + * (C) Copyright 2014-2015 The UnrealIRCd team (Syzop, DBoyz, Nath and others) + * + * License: GPLv2 + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER(certfp) + = { + "certfp", + "$Id$", + "Certificate fingerprint", + "3.2-b8-1", + NULL + }; + +/* Forward declarations */ +void certfp_free(ModData *m); +char *certfp_serialize(ModData *m); +void certfp_unserialize(char *str, ModData *m); +int certfp_connect(aClient *sptr); +int certfp_whois(aClient *sptr, aClient *acptr); + +ModDataInfo *certfp_md; /* Module Data structure which we acquire */ + +#define WHOISCERTFP_STRING ":%s 276 %s %s :has client certificate fingerprint %s" + +DLLFUNC int MOD_INIT(certfp)(ModuleInfo *modinfo) +{ +ModDataInfo mreq; + + memset(&mreq, 0, sizeof(mreq)); + mreq.name = "certfp"; + mreq.free = certfp_free; + mreq.serialize = certfp_serialize; + mreq.unserialize = certfp_unserialize; + mreq.sync = 1; + mreq.type = MODDATATYPE_CLIENT; + certfp_md = ModDataAdd(modinfo->handle, mreq); + if (!certfp_md) + abort(); + + HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, certfp_connect); + HookAddEx(modinfo->handle, HOOKTYPE_WHOIS, certfp_whois); + + return MOD_SUCCESS; +} + +DLLFUNC int MOD_LOAD(certfp)(int module_load) +{ + return MOD_SUCCESS; +} + + +DLLFUNC int MOD_UNLOAD(certfp)(int module_unload) +{ + return MOD_SUCCESS; +} + +/* + * Obtain client's fingerprint. + */ +char *get_fingerprint_for_client(aClient *cptr) +{ + unsigned int n; + unsigned int l; + unsigned char md[EVP_MAX_MD_SIZE]; + static char hex[EVP_MAX_MD_SIZE * 2 + 1]; + char hexchars[16] = "0123456789abcdef"; + const EVP_MD *digest = EVP_sha256(); + X509 *x509_clientcert = NULL; + + if (!MyConnect(cptr) || !cptr->ssl) + return NULL; + + x509_clientcert = SSL_get_peer_certificate((SSL *)cptr->ssl); + + if (x509_clientcert) + { + if (X509_digest(x509_clientcert, digest, md, &n)) { + int j = 0; + for (l=0; l> 4) & 0xF]; + hex[j++] = hexchars[md[l] & 0xF]; + } + hex[j] = '\0'; + X509_free(x509_clientcert); + return hex; + } + X509_free(x509_clientcert); + } + return NULL; +} + +int certfp_connect(aClient *acptr) +{ + if (IsSecure(acptr)) + { + char *fp = get_fingerprint_for_client(acptr); + void *data = &moddata_client(acptr, certfp_md); + + sendnotice(acptr, "*** Your SSL fingerprint is %s", fp); + + certfp_unserialize(fp, data); + } + return 0; +} + +int certfp_whois(aClient *sptr, aClient *acptr) +{ + char *fp; + void *data = &moddata_client(acptr, certfp_md); /* never NULL */ + + fp = certfp_serialize(data); + + if (fp) + sendto_one(sptr, WHOISCERTFP_STRING, me.name, sptr->name, acptr->name, fp); + return 0; +} + +void certfp_free(ModData *m) +{ + if (m->str) + MyFree(m->str); + m->str = NULL; +} + +char *certfp_serialize(ModData *m) +{ + if (!m->str) + return NULL; + return m->str; +} + +void certfp_unserialize(char *str, ModData *m) +{ + if (m->str) + MyFree(m->str); + m->str = strdup(str); +}