mirror of https://github.com/pissnet/pissircd.git
187 lines
3.9 KiB
C
187 lines
3.9 KiB
C
/*
|
|
* Certificate Fingerprint Module
|
|
* This grabs the SHA256 fingerprint of the 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 or later
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
|
|
ModuleHeader MOD_HEADER
|
|
= {
|
|
"certfp",
|
|
"5.0",
|
|
"Certificate fingerprint",
|
|
"UnrealIRCd Team",
|
|
"unrealircd-6",
|
|
};
|
|
|
|
/* Forward declarations */
|
|
void certfp_free(ModData *m);
|
|
const char *certfp_serialize(ModData *m);
|
|
void certfp_unserialize(const char *str, ModData *m);
|
|
int certfp_handshake(Client *client);
|
|
int certfp_connect(Client *client);
|
|
int certfp_whois(Client *client, Client *target, NameValuePrioList **list);
|
|
int certfp_json_expand_client(Client *client, int detail, json_t *j);
|
|
|
|
ModDataInfo *certfp_md; /* Module Data structure which we acquire */
|
|
|
|
MOD_INIT()
|
|
{
|
|
ModDataInfo mreq;
|
|
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
|
|
memset(&mreq, 0, sizeof(mreq));
|
|
mreq.name = "certfp";
|
|
mreq.free = certfp_free;
|
|
mreq.serialize = certfp_serialize;
|
|
mreq.unserialize = certfp_unserialize;
|
|
mreq.sync = MODDATA_SYNC_EARLY;
|
|
mreq.type = MODDATATYPE_CLIENT;
|
|
certfp_md = ModDataAdd(modinfo->handle, mreq);
|
|
if (!certfp_md)
|
|
abort();
|
|
|
|
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, certfp_connect);
|
|
HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, certfp_handshake);
|
|
HookAdd(modinfo->handle, HOOKTYPE_SERVER_HANDSHAKE_OUT, 0, certfp_handshake);
|
|
HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, certfp_whois);
|
|
HookAdd(modinfo->handle, HOOKTYPE_JSON_EXPAND_CLIENT, 0, certfp_json_expand_client);
|
|
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_LOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
|
|
MOD_UNLOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Obtain client's fingerprint.
|
|
*/
|
|
char *get_fingerprint_for_client(Client *client)
|
|
{
|
|
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(client) || !client->local->ssl)
|
|
return NULL;
|
|
|
|
x509_clientcert = SSL_get_peer_certificate(client->local->ssl);
|
|
|
|
if (x509_clientcert)
|
|
{
|
|
if (X509_digest(x509_clientcert, digest, md, &n)) {
|
|
int j = 0;
|
|
for (l=0; l<n; l++) {
|
|
hex[j++] = hexchars[(md[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_handshake(Client *client)
|
|
{
|
|
if (client->local->ssl)
|
|
{
|
|
char *fp = get_fingerprint_for_client(client);
|
|
|
|
if (!fp)
|
|
return 0;
|
|
|
|
moddata_client_set(client, "certfp", fp); /* set & broadcast */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int certfp_connect(Client *client)
|
|
{
|
|
if (IsSecure(client))
|
|
{
|
|
const char *fp = moddata_client_get(client, "certfp");
|
|
|
|
if (fp && !iConf.no_connect_tls_info)
|
|
sendnotice(client, "*** Your TLS certificate fingerprint is %s", fp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int certfp_whois(Client *client, Client *target, NameValuePrioList **list)
|
|
{
|
|
const char *fp = moddata_client_get(target, "certfp");
|
|
char buf[512];
|
|
|
|
if (!fp)
|
|
return 0;
|
|
|
|
if (whois_get_policy(client, target, "certfp") == WHOIS_CONFIG_DETAILS_FULL)
|
|
add_nvplist_numeric(list, 0, "certfp", client, RPL_WHOISCERTFP, target->name, fp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void certfp_free(ModData *m)
|
|
{
|
|
safe_free(m->str);
|
|
}
|
|
|
|
const char *certfp_serialize(ModData *m)
|
|
{
|
|
if (!m->str)
|
|
return NULL;
|
|
return m->str;
|
|
}
|
|
|
|
void certfp_unserialize(const char *str, ModData *m)
|
|
{
|
|
safe_strdup(m->str, str);
|
|
}
|
|
|
|
int certfp_json_expand_client(Client *client, int detail, json_t *j)
|
|
{
|
|
json_t *tls;
|
|
const char *str;
|
|
|
|
if (detail < 2)
|
|
return 0;
|
|
|
|
str = moddata_client_get(client, "certfp");
|
|
if (!str)
|
|
return 0;
|
|
|
|
tls = json_object_get(j, "tls");
|
|
if (!tls)
|
|
{
|
|
tls = json_object();
|
|
json_object_set_new(j, "tls", tls);
|
|
}
|
|
|
|
json_object_set_new(tls, "certfp", json_string_unreal(str));
|
|
|
|
return 0;
|
|
}
|